Как передать переменную, содержащую косую черту, в sed

102

Как передать в качестве шаблона переменную, содержащую косую черту sed?

Например, если у меня есть следующая переменная:

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

Я хочу передать это sedтак:

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

Однако я получаю ошибки. Как я могу обойти проблему?

Buydadip
источник

Ответы:

197

Используйте альтернативный разделитель регулярных выражений, так как sedпозволяет использовать любой разделитель ( включая управляющие символы ):

sed "s~$var~replace~g" $file
анубхава
источник
2
Не работает. Я пробую sed -i "s ~ blah ~ $ var ~ g file" и получаю: "sed -e expression # 1, char 70: unterminated` s 'command ". Я подтвердил, что виноват косая черта в $ var. Варианты этого решения есть повсюду, и ни один из них не работает. Был ли недавно обновлен sed? Я использую v4.2.2 на Ubuntu 14.04.4 LTS.
Пол Эриксон
Это зависит от стоимости$var
анубхава 01
1
Не работает с ~, как заметил @PaulEricson, но все же работает с |
Кенни Хо
1
Последний "~" (после g) кажется не нужен, по крайней мере, для AWS Amazon Linux (на базе CentOS)
Саул Мартинес Видалс
1
Ты спас мне день
user7131571
63

Чистый ответ bash: используйте расширение параметра, чтобы избежать косой черты в переменной:

var="/Users/Documents/name/file"
sed "s/${var//\//\\/}/replace/g" $file
Гленн Джекман
источник
1
Это хороший способ, потому что вы не всегда знаете, какие символы содержат переменную. Таким образом, вы всегда можете избежать разделителя sed, если есть сомнения. Например: sed "s: $ {var //: / \\:}: replace: g" $ file
Stephane L
Прекрасный. Сделал некоторые мои скрипты переименования намного чище.
Cloud
Узнал сегодня что-то красивое, спасибо. Должен быть принятый ответ.
Стив Кехлет
6
Некоторые ясности в шаблоне используется здесь: ${parameter/pattern/string}. Итак, в этом случае параметр var, шаблон /\/и строка \\/. Все экземпляры шаблона заменяются, потому что шаблон начинается с символа /.
Джош
1
@josh, и поэтому ведущая косая черта в шаблоне на самом деле не является частью шаблона, который нужно заменить.
Гленн Джекман 07
32

Другой способ сделать это, хотя и уродливее, чем ответ анубхавы, - это избежать всех обратных косых черт с varпомощью другой sedкоманды:

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

тогда это будет работать:

sed "s/$var/replace/g" $file
Buydadip
источник
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 file" и получаю: "sed -e expression # 1, char 70: unterminated` s 'command ". Я подтвердил, что виноват косая черта в $ var. Варианты этого решения есть повсюду, и ни один из них не работает. Был ли sed недавно обновлен? Я использую v4.2.2 на Ubuntu 14.04.4 LTS.
Пол Эриксон
@PaulEricson Я не могу воспроизвести это на любом доступном мне Ubuntu. Если ваш $varсодержит ~, вам, очевидно, нужно выбрать другой разделитель. "Неисправленная" ошибка звучит так, как будто ваша $varсодержит новую строку; вы могли бы исправить это, экранируя каждую новую строку в значении с помощью обратной косой черты, но я не думаю, что это полностью переносимо. Это по большому счету не связано с проблемой косой черты в значении.
tripleee
@PaulEricson О, и ваше цитирование неверно, имя файла не должно быть внутри кавычек. sed -i "s~blah~$var~g" fileс кавычками только вокруг самого sedсценария.
tripleee
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 в Stack Exchange. Все остальное эффективно «избегает всего, что является особенным для sed», что практически бесполезно, учитывая изменчивость переменных и sed.
Хит