Используйте переменную в качестве замены в команде bash sed - PullRequest
0 голосов
/ 16 марта 2020

Я использую команду sed в Ubuntu для замены содержимого.

Эта начальная команда происходит от здесь .

sed -i '$ s/$/ /replacement/' "$DIR./result/doc.md"

Однако, как вы видите, у меня есть sla sh в замене. Sla sh заставляет команду бросить:

sed: -e выражение # 1, символ 9: неизвестная опция для `s '

Более того, моя замена хранится в переменной.

Таким образом, следующее не будет работать из-за sla sh:

sed -i "$ s/$/ $1/" "$DIR./result/doc.md"

Как указано здесь и в двух экземплярах , я должен используйте другой разделитель. Если я попытаюсь с @:

   sed -i "$ s@$@ $1@" "$DIR./result/doc.md"

Выдает ошибку:

sed: -e выражение # 1, символ 42: неопределенная команда `s '

Мой вопрос:

Как использовать переменную в этой команде, а также другой разделитель, кроме /?

Ответы [ 3 ]

1 голос
/ 16 марта 2020

Не используйте sed здесь; perl и awk допускают более надежные подходы.

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

Краткий ответ: Использование perl

Ниже взято из BashFAQ # 21 :

inplace_replace() {
  local search=$1; shift; local replace=$1; shift
  in="$search" out="$replace" perl -pi -e 's/\Q$ENV{"in"}/$ENV{"out"}/g' "$@"
}
inplace_replace '@' "replacement" "$DIR/result/doc.md" 

Более длинный ответ: Использование awk

... или использование awk для выполнения потоковой замены и функция оболочки вместо этого сделать замену файла:

# usage as in: echo "in should instead be out" | gsub_literal "in" "out"
gsub_literal() {
  local search=$1 replace=$2
  awk -v s="${search//\\/\\\\}" -v r="${rep//\\/\\\\}" 'BEGIN {l=length(s)} {o="";while (i=index($0, s)) {o=o substr($0,1,i-1) r; $0=substr($0,i+l)} print o $0}'
}

# usage as in: inplace_replace "in" "out" /path/to/file1 /path/to/file2 ...
inplace_replace() {
  local search=$1 replace=$2 retval=0; shift; shift
  for file; do
    tempfile=$(mktemp "$file.XXXXXX") || { retval |= $?; continue; }
    if gsub_literal "$search" "$replace" <"$file" >"$tempfile"; then
      mv -- "$tempfile" "$file" || (( retval |= $? ))
    else
      rm -f -- "$tempfile" || (( retval |= $? ))
    fi
  done
}
0 голосов
/ 16 марта 2020

Используйте расширение параметра оболочки , чтобы добавить экранирование в косые черты в переменной:

$ cat file
foo
bar
baz

$ set -- ' /repl'

$ sed "s/$/$1/" file
sed: 1: "s/$/ /repl/": bad flag in substitute command: 'r'

$ sed "s/$/${1//\//\\\/}/" file
foo /repl
bar /repl
baz /repl

Это чудовищность наклоняющихся зубочисток, но она служит для преобразования этого:

sed "s/$/ /repl/"

в

sed "s/$/ \/repl/"

Та же техника может использоваться для того, что вы выберете в качестве разделителя sed s///.

0 голосов
/ 16 марта 2020

TL; DR:

Попробуйте:

sed -i '$ s@$@ '"$1"'@' "$DIR./result/doc.md"

Длинная версия:

Давайте начнем с вашего исходного кода:

sed -i '$ s/$/ /replacement/' "$DIR./result/doc.md"

И давайте сравним его с кодом, на который вы ссылались:

sed -i '$ s/$/abc/' file.txt

Мы видим, что они не совсем совпадают. Я вижу, что вы правильно сделали эту замену:

file.txt --> "$DIR./result/doc.md"

Это выглядит хорошо (хотя у меня есть сомнения относительно. После $ DIR). Однако другая подстановка выглядит не очень хорошо:

abc -->  /replacement

Вы фактически ввели другой разделитель /. Однако, если мы заменим разделители на '@', мы получим это:

sed -i '$ s@$@ /replacement@' "$DIR./result/doc.md"

Я думаю, что вышеприведенное совершенно верно в sed / bash. $@ не будет заменен оболочкой, поскольку он заключен в одинарные кавычки. Переменная $DIR будет интерполироваться оболочкой, поскольку она заключена в двойные кавычки.

Рассмотрим одну из ваших попыток:

sed -i "$ s@$@ $1@" "$DIR./result/doc.md"

У вас возникнут проблемы из-за к интерполяции оболочки $ @ в двойных кавычках. Давайте исправим это, заменив одинарными кавычками (но оставив $ 1 без кавычек):

sed -i '$ s@$@ '"$1"'@' "$DIR./result/doc.md"

Обратите внимание на «$ 1». Мне пришлось окружить 1 доллар символом '', чтобы в основном убрать окружающие одинарные кавычки. Но затем я окружил $ 1 двойными кавычками, чтобы мы могли защитить строку от пробелов.

...