Видаліть фіксований префікс / суфікс із рядка в Bash


484

У своєму bashскрипті у мене є рядок та його префікс / суфікс. Мені потрібно видалити префікс / суфікс із початкового рядка.

Наприклад, скажімо, у мене є такі значення:

string="hello-world"
prefix="hell"
suffix="ld"

Як дійти до наступного результату?

result="o-wor"


14
Будьте дуже обережні при посиланні на так званий Посібник з сценаріїв розширеного тексту; вона містить суміш гарних порад і жахливих.
tripleee

Відповіді:


718
$ foo=${string#"$prefix"}
$ foo=${foo%"$suffix"}
$ echo "${foo}"
o-wor

40
Існують також ## і %%, які максимально видаляють, якщо префікс $ або суфікс $ містять символи.
пт

28
Чи є спосіб поєднати два в один рядок? Я спробував, ${${string#prefix}%suffix}але це не працює.
static_rtti

28
@static_rtti Ні, на жаль, ви не можете вбудувати заміну параметрів, як це. Я знаю, це соромно.
Адріан Фрюхвірт

87
@ AdrianFrühwirth: вся мова ганьба, але це так корисно :)
static_rtti

8
Nvm, "заміна баша" в Google знайшла те, що я хотів.
Тайлер

89

Використання sed:

$ echo "$string" | sed -e "s/^$prefix//" -e "s/$suffix$//"
o-wor

У межах команди команда sed ^ символ символу відповідає тексту, що починається з $prefix, а $текст, що закінчується, відповідає тексту $suffix.

У коментарях нижче Адріан Фрюхвірт зазначає кілька хороших моментів, але sed для цього може бути дуже корисним. Той факт, що вміст префікса $ і суфікса $ інтерпретується sed, може бути і хорошим, АБО поганим - доки ви звернете увагу, вам слід добре. Краса полягає в тому, що ви можете зробити щось подібне:

$ prefix='^.*ll'
$ suffix='ld$'
$ echo "$string" | sed -e "s/^$prefix//" -e "s/$suffix$//"
o-wor

що може бути тим, що ви хочете, і є більш фантазійним і потужнішим, ніж заміна змінної bash. Якщо ви пам’ятаєте, що з великою силою наступає велика відповідальність (як каже Павук), вам слід добре.

Швидкий вступ до sed можна знайти на веб-сайті http://evc-cit.info/cit052/sed_tutorial.html

Примітка щодо оболонки та її використання струн:

Для конкретного наведеного прикладу також працює наступне:

$ echo $string | sed -e s/^$prefix// -e s/$suffix$//

... але тільки тому, що:

  1. ехо неважливо, скільки рядків у списку аргументів, і
  2. У префіксі $ та суфіксі $ немає пробілів

Як правило, добре цитувати рядок у командному рядку, оскільки навіть якщо він містить пробіли, він буде представлений команді як єдиний аргумент. Ми цитуємо $ префікс і $ суфікс з тієї ж причини: кожна команда редагування в sed буде передана як один рядок. Ми використовуємо подвійні лапки, оскільки вони допускають змінну інтерполяцію; якби ми використовували одинарні лапки, команда sed отримала б буквальний характер, $prefixі $suffixце, звичайно, не те, чого ми хотіли.

Зауважте також моє використання одиничних лапок при встановленні змінних prefixта suffix. Ми, звичайно, не хочемо, щоб нічого в рядках було інтерпретовано, тому ми цитуємо їх, щоб не відбулося інтерполяції. Знову ж, у цьому прикладі це може і не знадобитися, але це дуже гарна звичка.


8
На жаль, це погана порада з кількох причин: 1) Без котирування, $stringце підлягає розщепленню слів та поглинанню. 2) $prefixі $suffixможе містити вирази, які sedбудуть інтерпретувати, наприклад, регулярні вирази або символ, що використовується як роздільник, який порушить всю команду. 3) Дзвонити sedдва рази не потрібно (можна -e 's///' -e '///'замість цього), і трубу також можна уникнути. Наприклад, розгляньте string='./ *'та / або prefix='./'побачите, як вона жахливо зламається через 1)та 2).
Адріан Фрюхвірт

Примітка: sed може сприймати майже будь-що як роздільник. У моєму випадку, оскільки я розбирав префікси-каталоги поза шляхами, я не міг користуватися /, тому використовував sed "s#^$prefix##замість цього. (Крихкість: імена файлів не можуть містити #. Оскільки я контролюю файли, ми в безпеці, там.)
Olie,

@Olie Імена файлів можуть містити будь-який символ, окрім косої та нульової символів, тому, якщо ви не керуєте, ви не можете припустити, що ім'я файлу не містить певних символів.
Адріан Фрюхвірт

Так, не знаю, що я там думав. iOS, можливо? Данно. Імена файлів, безумовно, можуть містити "#". Не знаю, чому я це сказав. :)
Олі,

@Olie: Коли я зрозумів ваш оригінальний коментар, ви говорили, що обмеження вашого вибору використовувати #як роздільник сім'ї означає, що ви не можете обробляти файли, що містять цей символ.
P тато

17

Чи знаєте ви довжину вашого префікса та суфікса? У вашому випадку:

result=$(echo $string | cut -c5- | rev | cut -c3- | rev)

Або більш загальне:

result=$(echo $string | cut -c$((${#prefix}+1))- | rev | cut -c$((${#suffix}+1))- | rev)

Але рішення від Адріана Фрюхвірта - це здорово! Я про це не знав!


14

Я використовую grep для видалення префіксів із шляхів (які не обробляються добре sed):

echo "$input" | grep -oP "^$prefix\K.*"

\K вилучає з матчу всіх персонажів перед ним.


grep -Pє нестандартним розширенням. Більше потужності для вас, якщо він підтримується на вашій платформі, але це сумнівна порада, якщо ваш код повинен бути досить портативним.
трійчатка

@tripleee Дійсно. Але я думаю, що в системі з встановленим GNU Bash також є греп, який підтримує PCRE.
Володимир Петракович

1
Ні, MacOS, наприклад, не має Bash, але не GNU grep. Раніші версії насправді мали -Pопцію від BSD, grepале вони її видалили.
трійка

9
$ string="hello-world"
$ prefix="hell"
$ suffix="ld"

$ #remove "hell" from "hello-world" if "hell" is found at the beginning.
$ prefix_removed_string=${string/#$prefix}

$ #remove "ld" from "o-world" if "ld" is found at the end.
$ suffix_removed_String=${prefix_removed_string/%$suffix}
$ echo $suffix_removed_String
o-wor

Примітки:

# префікс $: додавання # гарантує, що підстрочка "пекло" буде видалена лише в тому випадку, якщо вона знайдена на початку. Суфікс% $: додавання% гарантує, що підряд "ld" буде видалений лише в тому випадку, якщо він знайдеться в кінці.

Без них підрядки "пекло" та "ld" будуть видалені скрізь, навіть вони знаходяться посередині.


Дякуємо за Примітки! qq: у прикладі коду ви також маєте нахилену лінію наперед/ лінії після рядка, для чого це?
Дієго Салазар

1
/ розділяє поточний рядок і підряд. підрядковий рядок - це суфікс у розміщеному запитанні.
Віджай Ват


6

Невелике і універсальне рішення:

expr "$string" : "$prefix\(.*\)$suffix"

1
Якщо ви використовуєте Bash, ви, ймовірно, взагалі не використовуєте expr. Це була якась зручна утиліта для кухонних мийок ще за часів оригінальної оболонки Bourne, але зараз пройшла повз її найкращу дату.
трійчатка

5

Використовуючи відповідь @Adrian Frühwirth:

function strip {
    local STRING=${1#$"$2"}
    echo ${STRING%$"$2"}
}

використовувати його так

HELLO=":hello:"
HELLO=$(strip "$HELLO" ":")
echo $HELLO # hello

0

Я б скористався групами захоплення в регулярному виразі:

$ string="hello-world"
$ prefix="hell"
$ suffix="ld"
$ set +H # Disables history substitution, can be omitted in scripts.
$ perl -pe "s/${prefix}((?:(?!(${suffix})).)*)${suffix}/\1/" <<< $string
o-wor
$ string1=$string$string
$ perl -pe "s/${prefix}((?:(?!(${suffix})).)*)${suffix}/\1/g" <<< $string1
o-woro-wor

((?:(?!(${suffix})).)*)гарантує, що вміст ${suffix}буде виключено із групи захоплення. З точки зору прикладу, це рядок, еквівалентний [^A-Z]*. Інакше ви отримаєте:

$ perl -pe "s/${prefix}(.*)${suffix}/\1/g" <<< $string1
o-worldhello-wor
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.