Як передати змінну, що містить косу рису, до sed


100

Як ви передаєте змінну, що містить риски в якості шаблону sed?

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

var="/Users/Documents/name/file"

Я хочу передати це sedтак:

sed "s/$var/replace/g" "$file"

Однак я отримую помилки. Як я можу обійти це питання?

Відповіді:


193

Використовуйте альтернативний роздільник розміру регулярних виразів, оскільки sedдозволяє використовувати будь-який роздільник ( включаючи контрольні символи ):

sed "s~$var~replace~g" $file

2
Не працює. Я намагаюся sed -i "s ~ blah ~ $ var ~ g файл" і отримую: "sed -e вираз №1, char 70: untermined` s 'command ". Я підтвердив, що винуватцем є косоокість у $ var. Варіанти цього рішення для цього є повсюдно, і жоден з них не працює. Нещодавно оновлено sed? Я на v4.2.2 на Ubuntu 14.04.4 LTS.
Пол Еріксон

Це залежить від того, яке значення$var
anubhava

1
Не працює з ~, як зауважив @PaulEricson, але все ще працює з |
Кенні Хо

1
Останній "~" (після г), здається, не потрібен, принаймні для AWS Amazon Linux (на основі CentOS)
Saúl Martínez Vidals

1
Ти врятував мій день
user7131571

62

Чистий відповідь bash: використовуйте розширення параметрів для зворотної косої риски будь-якої косої риси змінної:

var="/Users/Documents/name/file"
sed "s/${var//\//\\/}/replace/g" $file

1
Це хороший спосіб, адже ви не завжди знаєте, які символи містить змінну. Таким чином, ви завжди можете уникнути розмежувача sed, якщо є сумніви. Наприклад: sed "s: $ {var //: / \\:}: Замінити: g" $ файл
Стефан L

Гарний. Зробив кілька сценаріїв з перейменовуванням шахти набагато чистішими.
Хмара

Навчився чогось прекрасного сьогодні, дякую. Повинна бути прийнята відповідь.
Стів Келет

6
Деякі ясності в шаблоні використовується тут: ${parameter/pattern/string}. Отже, у цьому випадку параметр є var, шаблон є /\/, а рядок - \\/. Усі екземпляри шаблону замінюються, оскільки шаблон починається з а /.
Джош

1
@josh, і тому провідна косої частини шаблону насправді не є частиною шаблону для заміни.
Гленн Джекман

32

Інший спосіб зробити це, хоча і гірше, ніж відповідь на анубхаву, - це уникнути всіх зворотних нахилів при varвикористанні іншої sedкоманди:

var=$(echo "$var" | sed 's/\//\\\//g')

тоді це спрацює:

sed "s/$var/replace/g" $file

12

Використання /sed в якості роздільника буде конфліктувати з косою рисою змінної при її заміні, і в результаті ви отримаєте помилку. Один із способів обійти це - використовувати інший роздільник, унікальний для будь-яких символів, що знаходяться в цій змінній.

var="/Users/Documents/name/file"

ви можете використовувати персонаж октоторпа, який підходить для випадку (або будь-який інший персонаж, який не є зручним /для використання)

sed "s#$var#replace#g" 

або

sed 's#$'$var'#replace#g'

це підходить, коли змінна не містить пробілів

або

sed 's#$"'$var'"#replace#g'

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


Не працює. Я намагаюся sed -i "s ~ blah ~ $ var ~ g файл" і отримую: "sed -e вираз №1, char 70: untermined` s 'command ". Я підтвердив, що винуватцем є косоокість у $ var. Варіанти цього рішення для цього є повсюдно, і жоден з них не працює. Нещодавно оновлено sed? Я на v4.2.2 на Ubuntu 14.04.4 LTS.
Пол Еріксон

@PaulEricson Я не можу спростувати це на будь-якому доступному мені Ubuntu. Якщо ваш $varвміст містить ~, вам, очевидно, потрібно вибрати інший роздільник. Помилка "незакінченої" звучить так, що ваша $varмістить новий рядок; ви зможете це виправити шляхом зменшення косої риски кожного нового рядка у значенні, але я не думаю, що це повністю портативно. Це за великим рахунком не пов'язане з проблемою косої риски у значенні.
трійка

@PaulEricson О, а цитування у вас неправильне, ви не повинні мати ім'я файлу всередині лапок. sed -i "s~blah~$var~g" fileз цитатами лише навколо фактичного sedсценарію.
трійчатка

5

Це давнє запитання, але жоден з відповідей тут не обговорює інших операцій, ніж s/from/to/дуже детально.

Загальною формою sedзаяви є

*address* *action*

де адресою може бути діапазон регулярних виразів або діапазон номерів рядка (або порожній; у цьому випадку дія застосовується до кожного вхідного рядка). Так, наприклад

sed '1,4d' file

видалить рядки з 1 по 4 ( адреса - це діапазон номерів рядків, 1,4а дія - команда dвидалення); і

sed '/ick/,$s/foo/bar/' file

замінить перше входження fooз barбудь-якою лінією між першим матчем на регулярному виразі ickі в кінці файлу ( адреса є діапазоном /ick/,$і дією є sкомандою заміни s/foo/bar/).

У цьому контексті, якщо ви ickприйшли зі змінної, ви могли б зробити

sed "/$variable/,\$s/foo/bar/"

(зауважте використання подвійних лапок замість одинарних, щоб оболонка могла інтерполювати змінну та необхідність цитувати буквальний знак долара всередині подвійних лапок), але якщо змінна містить косу рису, ви отримаєте синтаксичну помилку. (Оболонка розширює змінну, після чого передає отриманий рядок sed; тому sedбачить лише буквальний текст - у нього немає поняття змінних оболонки.)

Лікування полягає у використанні іншого роздільника (де, очевидно, вам потрібно вміти використовувати символ, який не може зустрічатися у значенні змінної), але на відміну від s%foo%bar%випадку, вам також потрібна зворотна косої риски перед роздільником, якщо ви хочете використовувати інший роздільник ніж за замовчуванням /:

sed "\\%$variable%,\$s/foo/bar/" file

(всередині одинарних лапок очевидно вистачить однієї зворотної косої риси); або ви можете окремо уникати кожної косої риски у значенні. Цей синтаксис є лише Bash:

sed "/${variable//\//\\/}/,\$s/foo/bar/" file

або якщо ви використовуєте іншу оболонку, спробуйте

escaped=$(echo "$variable" | sed 's%/%\\/%g')
sed "s/$escaped/,\$s/foo/bar/" file

Для наочності, якщо $variableміститься рядок, 1/2то наведені вище команди будуть еквівалентні

sed '\%1/2%,$s/foo/bar/' file

у першому випадку, і

sed '/1\/2/,$s/foo/bar/' file

у другій.


4

Використовуйте Perl, де змінні є громадянами першого класу, а не просто розширюють макроси:

var=/Users/Documents/name/file perl -pe 's/\Q$ENV{var}/replace/g' $file
  • -p зчитує рядок введення за рядком і друкує рядок після обробки
  • \Qцитує всі метахарактери у наступному рядку (не потрібний для значення, представленого тут, але необхідний, якщо значення, яке міститься, [або якісь інші значення, спеціальні для регулярних виразів)

Єдина загальнокорисна відповідь на десятки питань "використання змінних у виразі sed" на всій стек-біржі. Все інше - ефективно «уникнути всього, що є особливим для sed», що практично марно, враховуючи мінливість змінних та sed.
Хіт
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.