Заменить первое появление шаблона в файле, который может содержать косую черту

Re: ошибка 23 - Наиболее частая причина появления этой ошибки - допустить небольшую опечатку в исходном коде rsync. Просмотрите свою исходную команду и убедитесь, что все проверено на ls, и поищите глупые тонкие вещи, такие как лишнее пространство или 1-литровую проблему.

6
23.05.2017, 15:40
2 ответа

В то время как:

sed "0,\~$var~s~$var~replacement~"

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

Во-первых, здесь ограничитель - не единственный символ, который нужно экранировать. Это также необходимо для всех операторов регулярных выражений.

Но что более важно, особенно с GNU sed , это уязвимость, связанная с внедрением команд. Если содержимое $ var не находится под вашим контролем, это так же плохо, как передача произвольных данных в eval .

Попробуйте, например:

$ var='^~s/.*/uname/e;#'
$ echo | sed "0,\~$var~s~$var~replacement~"
Linux

Была запущена команда uname , к счастью, безобидная ... на этот раз.

Реализации, отличные от GNU sed , не могут запускать произвольные команды, но могут перезаписывать любой файл (с помощью команды w ), что практически так же плохо.

Более правильный способ - экранировать проблемные символы в $ var сначала :

NL='
'
case $var in
  (*"$NL"*)
    echo >&2 "Sorry, can't handle variables with newline characters"
    exit 1
esac
escaped_var=$(printf '%s\n' "$var" | sed 's:[][\/.^$*]:\\&:g')
# and then:
sed "0,/$escaped_var/s/$escaped_var/replacement/" < file

Другой подход - использовать perl :

var=$var perl -pe 's/\Q$ENV{var}\E/replacement/g && $n++ unless $n' < file

Примечание что мы не расширяем содержимое $ var внутри кода, переданного в perl (что может быть еще одной уязвимостью внедрения команд), но разрешаем perl расширяться его содержимое как часть обработки регулярного выражения (и в пределах \ Q ... \ E , что означает, что операторы регулярного выражения не обрабатываются специально).

Если $ var содержит символы новой строки, они могут совпадать только в том случае, если в конце есть только один.В качестве альтернативы можно передать параметр -0777 , чтобы ввод обрабатывался как отдельная запись, а не построчно.

10
27.01.2020, 20:23

Подобно решению Стефана perl, awk может избежать проблемы защиты "опасного" интерполированного значения с помощью альтернативного подхода:

export pattern="anypattern" # or whatever shell quoting is needed
awk 'NR==1,$0~ENVIRON["pattern"] {sub(ENVIRON["pattern"],"replacement")} 1' input
# or gsub if you want multiple matches on the first line with any match

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

awk '!x&&$0~ENVIRON["pattern"] {sub(ENVIRON["pattern"],"replacement");x=1} 1' input
# ditto

Напоминание: awk (или sed) специально обрабатывает & в значении замены; если вам нужен фактический & префикс с \ (который обычно должен быть сначала закавычен; здесь это '') и аналогично удвоить фактический \

1
27.01.2020, 20:23

Теги

Похожие вопросы