Як "скинути" / видалити символи перед рядком?


13

У мене є струна, якою я хотів би маніпулювати. Рядок полягає в тому, H08W2345678як я міг би ним маніпулювати, щоб результат був просто W2345678?

Точно так само, якби я хотів скинути останні 4 символи, H08W2345678щоб я отримав, H08W234як би це зробити?


1
Існує багато способів маніпулювання струнами. Чи є конкретна причина використання sed?
don_crissti

@don_crissti Ні за що, крім браку досвіду, немає ніяких причин. Будь-які альтернативи вітаються ...
3kstc

@don_crissti, історія: із відфільтрованого CSV-файлу я беру один із параметрів із рядка, який є, H08W2345678і потрібно маніпулювати ним, до W2345678цього значення з іншими даними буде передано повідомлення, надіслане електронною поштою. З електронною поштою електронну пошту буде здійснюватись cron.
3kstc

@don_crissti awking. Я створюю масив, а потім змінюю кожен елемент у масиві (все по-різному - тобто міняю часову мітку Epoch у секундах на дату тощо)
3kstc,

2
Ви можете робити подібні речі з awk:printf %s\\n "XX,H08W2345678,YY" | awk -F, '{print substr($2, 4); print substr($2, 1, length($2)-4)}'
don_crissti

Відповіді:


19

Просто використовуючи bash (або ksh93звідки цей синтаксис походить або zsh):

string="H08W2345678"

echo "${string:3}"
W2345678

echo "${string:0:-4}"
H08W234

Докладнішу інформацію про маніпуляції зі струнами див. У Вікі Wooledge .


Для цього потрібно bash 4.2 або вище. Дивіться цю стару копію Довідкового посібника Баша, Розділ 3.5.3, "" Розширення параметра оболонки " або відповідь пташенят тут, щоб побачити старе обмеження (" довжина повинна оцінюватися до числа, що більше або дорівнює нулю. "); … (Продовження)
Скотт

(Продовжуйте)… див. Зміни Bash (у Вікі Bash Hackers) (прокрутіть донизу розділу) або новини в ефірі в організації «Технологічні інфраструктурні послуги» в Університеті Case Western Reserve (пошук «додано до bash-4.2» а потім прокрутіть униз до “q.”), щоб побачити версію. …………  "${string:0:${#string}-4}" Працює в баш-версії 4.1 до тих пір, поки довжина $stringне менше 4.
Скотт,

PS Це також задушиться на рядках типу abc-e, де, коли ви скидаєте перші три символи, вам залишається -e(бо echo -eне робить те, що ви хотіли).
Скотт

8
$ echo "H08W2345678" | sed 's/^.\{3\}//'
W2345678

sed 's/^.\{3\}//'знайде перші три символи ^.\{3\}і замінить їх порожнім. Тут ^.буде відповідати будь-який символ на початку рядка ( ^вказує на початок рядка) і \{3\}відповідатиме попередньому шаблону рівно 3 рази. Отже, ^.\{3\}підійдуть перші три символи.

$ echo "H08W2345678" | sed 's/.\{4\}$//'
H08W234

Аналогічно sed 's/.\{4\}$//'замінить останні чотири символи порожнім ( $вказує кінець рядка).


1
Поясніть, будь ласка, 's/^.\{3\}//'і, 's/.\{4\}$//'як я ще вивчаю сед, велике спасибі
3kstc

@ 3kstc: Перевірте правки
heemayl

1
Для кількох символів я б використав ...замість того, що .\{3\}(мені) це легше читати: sed -e 's/^...//' -e 's/....$//' або в одному виразі з чергуванням : sed -r 's/^...|....$//g'. Якщо для видалення було більше декількох символів, тоді я б використав /.\{17}\/вираз замість /.............../.
Джонні

Це буде поводитись погано, якщо рядок є -eабо -n. Звичайно, сенс «падіння останніх 4 -х символів» не визначене для рядка коротше 4 -х символів, але, якщо хто - то хотів , щоб пристосувати це кинути перший або останній один символ, це може підірвати.
Скотт

2

Якщо у вас є файл, у якому кожен рядок - це одинадцять символів (або будь-який інший) рядок, який ви хочете подрібнити, sedце інструмент для використання. Це добре для маніпулювання однією струною, але це надмірно. Щодо однієї рядка, відповідь Джейсона, мабуть, найкраща, якщо у вас є доступ до bash версії 4.2 або вище. Однак, синтаксиси і, здається, є унікальними для bash (ну, bash, ksh93, mksh і zsh) - я не бачу їх у специфікаціях Open Group Base для мови команд Shell . Якщо ви застрягли з оболонкою, сумісною з POSIX, яка не підтримує розширення (вилучення) підрядків, ви можете використовувати${parameter:offset}${parameter:offset:length}

$ printf "%s\n" "${string#???}"
W2345678

$ printf "%s\n" "${string%????}"
H08W234

використовуючи printfзамість того, echoщоб захистити від рядків типу abc-e, де, коли ви скидаєте перші три символи, вам залишається -eecho -eне робить те, що ви хотіли).

І якщо ви взагалі не використовуєте оболонку сімейства Борна (або ви використовуєте стародавню, до POSIX систему), вони все одно повинні працювати:

$ expr " $string" : ' ...\(.*\)'
W2345678

$ expr " $string" : ' \(.*\)....'
H08W234

Додаткове провідне простір , щоб уникнути проблем зі значеннями , $string які є фактичними exprоператорами (наприклад, +,  /,  indexабо match) або опціями (наприклад,  --, --helpабо  --version).


@ Stéphane Chazelas: (1) Дякую, що нагадав мені про підводний камінь, про який я знав близько 40 років тому і якийсь спосіб вдалося забути. (2) я завжди вирішував це за допомогою X; наприклад, expr "X$string" : 'X...\(.*\)'. ІМО, це простіше читати та розуміти. Чи є якась проблема з цим чи будь-яка причина віддати перевагу пробілу? (3) Сьогодні я дізнався, що expr + "$string" : '...\(.*\)'зараз працює. Я не пам’ятаю цього з 40 років тому; чи достатньо широко використовується, щоб його можна було безпечно рекомендувати? (4) Ви пропустили записку щодо відповіді Джазонвріана та відповідь "Ніт" на відповідь Хемайла.
Скотт

AFAIK, тобто expr +лише GNU (не працюватиме на Solaris і FreeBSD AFAICS). Я використовую простір замість x, оскільки менша ймовірність, що деяка exprреалізація матиме операторів, які починаються з простору, ніж з, xа також тому, що менш ймовірно, що будуть елементи, що починаються з простору, ніж з простору x. Але тоді я розумію, що це, мабуть, не вдалий вибір для expr " $a" "<" " $b"порівняння рядків, оскільки деякі реалізації в кінцевому підсумку роблять числове порівняння, коли $a/ $bвиглядають як числа. Можливо, expr "@@$a"...або expr "x $a"може бути безпечніше.
Стефан Шазелас

0

З:

string="H08W2345678"

Збіг 3 або 4 символів здається простим (для більшості оболонок):

$ printf '%s\t%s\n' "${string#???}" "${string%????}"
W2345678      H08W234

Для старих оболонок (наприклад, оболонки Борна) використовуйте:

$ string=H08W2345678

$ expr " ${string}" : " ...\(.*\)"
W2345678

$ expr " ${string}" : " \(.*\)...." '
H08W234

Якщо вам потрібно числове число символів, використовуйте:

$ expr " ${string}" : " .\{3\}\(.*\)"
W2345678

$ expr " ${string}" : " \(.*\).\{4\}" '
H08W234

Звичайно, ці регулярні вирази також працюють з sed, awk та bash 3.0+:

$ echo "$string" | sed 's/^.\{3\}//'
W2345678

$ echo "$string" | sed 's/.\{4\}$//'
H08W234

$ echo "$string" | awk '{sub(/^.{3}/,"")}1'
W2345678

$ echo "$string" | awk '{sub(/.{4}$/,"")}1'
H08W234

$ r='^.{3}(.*)$'; [[ $a =~ $r ]] && echo "${BASH_REMATCH[1]}"
W2345678

$ r='^(.*).{4}$'; [[ $a =~ $r ]] && echo "${BASH_REMATCH[1]}"
H08W234

-1

Як "скинути" / видалити символи перед рядком?

У мене є струна, якою я хотів би маніпулювати. Рядок H08W2345678, як я міг би ним маніпулювати, щоб вихід був просто W2345678?

echo "H08W2345678" | cut -c 4-

Це відповідає лише на половину питання.
Kusalananda

Я вважаю, що ваш внесок є несправедливим Це половина відповідає на питання , я мав , коли я гугл POSIX видалити перші символи і ця сторінка з'явилася в результатах пошуку. Більше того, назва цієї сторінки охоплює лише таку половину питання. Я повернувся і зробив свій внесок, коли знайшов рішення, яке мені сподобалось - я вважаю, що ця робота cutє набагато елегантнішою, ніж все, що є на цій сторінці.
aexl
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.