Как правильно развернуть переменную Bash, которая содержит новые строки на стороне замены (вставки) sed - PullRequest
0 голосов
/ 08 января 2019

Сначала потерпи, спасибо. Предположим, у меня есть

$ echo $'foo\nbar'
foo
bar

Теперь, когда я присваиваю строку переменной Bash, Bash больше не дает такой же вертикальный вывод:

$ str='foo\nbar'
$
$ echo $str
foo\nbar
$
$ echo $'str'
str

Попробуйте printf:

$ printf "$str\n"
foo
bar

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

# this does not work:
sed -i.bak $'/<!-- insert here -->/i\\\n'$'str'$'\\\n' index.html

# this works as expected though:
sed -i.bak $'/<!-- insert here -->/i\\\n'foo$'\\\n'bar$'\\\n' index.html

Я сделал несколько способов взломать это, но ни один из них не сработал; Вот один пример:

# this does not work:
sed -i.bak $'/<!-- insert here -->/i\\\n'`printf 'foo\\x0Abar'`$'\\\n' index.html

Дальнейшие тесты, я понял, что, пока переменная не содержит новых строк, все работает как положено:

# This works as long as str2 does not contain any newline.
str2='foo_bar'
sed -i.bak $'/<!-- insert here -->/i\\\n'$str2$'\\\n' index.html

Ожидаемый результат состоит в том, что sed вставит 2 вкладыша перед <! - вставкой здесь -> файла index.html.

foo
bar
<!-- insert here -->

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

Это выполнимо или нет?

Моя система - macOS High Sierra 10.13.6
Bash версия: 3.2.57 (1) -релиз
Последнее обновление BSD sed - 10 мая 2005 г.

Ответы [ 3 ]

0 голосов
/ 08 января 2019

Используйте sed команду r для вставки произвольного текста

str="abc\ndef"

tmp=$(mktemp)
(
   echo
   printf -- "$str"
   echo
) > "$tmp"

sed -i.bak '/<!-- insert here -->/r '"$tmp" index.html

rm -r "$tmp"

sed интерпретирует символ новой строки как разделитель команд. ; на самом деле не является разделителем команд sed s, только перевод строки. Не добавляйте / суффикс ; или } или пробелы в команде w - это будет интерпретироваться как часть имени файла (да, пробелы также). sed команды, такие как w или r, экранируются новой строкой.

Если вы хотите большей гибкости, перейдите к awk.

0 голосов
/ 08 января 2019

В ваших примерах есть несколько незначительных ошибок, поэтому вот несколько примеров, касающихся цитирования и новых строк в строках в bash и sed.

Как работает цитирование в целом:

# bash converts escape-sequence '\n' to real newline (0x0a) before passing it to echo
$ echo $'foo\nbar'
foo
bar

# bash passes literal 8 characters 'foo\nbar' to echo and echo simply prints them
$ echo 'foo\nbar'
foo\nbar

# bash passes literal 8 characters 'foo\nbar' to echo and echo converts escape-sequence
$ echo -e 'foo\nbar'
foo
bar

# bash passes literal string 'foo\nbar' to echo (twice)
# then echo recombines both arguments using a single space
$ str='foo\nbar'
$ echo $str        "$str"
foo\nbar foo\nbar

# bash interprets escape-sequences and stores result 'foo<0x0a>bar' in str,
# then passes two arguments 'foo' and 'bar' to echo, due to "word splitting"
# then echo recombines both arguments using a single space
$ str=$'foo\nbar'
$ echo $str
foo bar

# bash interprets escape-sequences and stores result 'foo<0x0a>bar' in str,
# then passes it as a single argument to echo, without "word splitting"
$ str=$'foo\nbar'
$ echo "$str"
foo
bar

Как применить кавычки оболочки при работе с символами новой строки в sed

# replace a character with newline, using newline's escape-sequence
# sed will convert '\n' to a literal newline (0x0a)
$ sed 's/-/foo\nbar/' <<< 'blah-blah'

# replace a character with newline, using newline's escape-sequence in a variable
# sed will convert '\n' to a literal newline (0x0a)
$ str='foo\nbar' # str contains the escape-sequence '\n' and not a literal newline
$ sed 's/-/'"$str"'/' <<< 'blah-blah'

# replace a character with newline, using a literal newline.
# note the line-continuation-mark \ after 'foo' before the literal newline,
# which is part of the sed script, since everything in-between '' is literal
$ sed 's/-/foo\
bar/' <<< 'blah-blah' # end-of-command

# replace a character with newline, using a newline in shell-escape-mode
# note the same line-continuation-mark \ before $'\n', which is part of the sed script
# note: the sed script is a single string composed of three parts '…\', $'\n' and '…',
$ sed 's/-/foo\'$'\n''bar/' <<< 'blah-blah'

# the same as above, but with a single shell-escape-mode string instead of 3 parts.
# note the required quoting of the line-continuation-mark with an additional \ escape
# i.e. after shell-escaping the sed script contains a single \ and a literal newline
$ sed $'s/-/foo\\\nbar/' <<< 'blah-blah'

# replace a character with newline, using a shell-escaped string in a variable
$ str=$'\n' # str contains a literal newline (0x0a) due to shell escaping
$ sed 's/-/foo\'"$str"'bar/' <<< 'blah-blah'

# same as above with the required (quoted) line-continuation inside the variable
# note, how the single \ from '…foo\' (previous example) became \\ inside $'\\…'
$ str=$'\\\n' # str contains \ and a literal newline (0x0a) due to shell escaping
$ sed 's/-/foo'"$str"'bar/' <<< 'blah-blah'

Все примеры sed будут напечатаны одинаково:

blahfoo
barblah

Итак, новая строка в строке замены sed должна быть (1) escape-последовательность новой строки (т.е. '\n'), поэтому sed может заменить ее буквальной новой строкой, или (2) буквальный символ новой строки, которому предшествует знак продолжения строки (т. Е. $'\\\n' или '\'$'\n', который НЕ совпадает с '\\\n' или '\\n' или $'\\n').

Это означает, что вам нужно заменить каждый буквенный символ новой строки <0x0a> на escape-последовательность новой строки \n или вставить знак продолжения строки перед каждым буквенным символом новой строки внутри вашей замены строка перед двойными кавычками-расширениями в замещающую строку sed.

Поскольку есть еще много предостережений относительно экранирования в sed, я рекомендую использовать функцию awk *1030* вместо передачи строки замены в качестве переменной через -v, например,

$ str=$'foo\nbar'
$ awk -v REP="$str" -- '{gsub(/-/, REP); print}' <<< 'blah-blah'
blahfoo
barblah

PS: Я не знаю, является ли этот ответ полностью верным в вашем случае, потому что ваша операционная система использует устаревшую версию bash.

0 голосов
/ 08 января 2019
echo -e $str

где -e

  • включить интерпретацию обратной косой черты
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...