Как разбить команды sed на вложенные части в среде Bash?

В этом «объявлении» упоминается версия 6.3, поэтому оно выглядит подлинным.

0
15.03.2021, 05:10
5 ответов

Не уверен, что это будет более читабельно для вас, но вы можете построить выражение sedсначала с помощью printf, а затем использовать его сsed:

sed_expr=$(printf 's/$to = ".*";$/$to = "%s";/' "$new_email_address")
sed -i "$sed_expr" FILE

Таким образом, ИМО, легче увидеть, что sed будет делать в целом, а также какую роль в нем будет играть ввод.

2
18.03.2021, 22:25

Здесь я бы использовал perl. -i— это нестандартная опция -, которую некоторые реализации sedскопировали из perl, но она не переносима. Использование sedтаким образом также является уязвимостью для внедрения команд, поскольку содержимое $new_email_addressв конечном итоге интерпретируется как sedкод (, а в языке GNU sedесть команды, которые могут запускать произвольные команды, попробуйте экземпляр, вводящий /;ereboot;#в этом приглашении read).

IFS= read -r new_email_address

REPLACEMENT="$new_email_address" perl -pi -e '
  s{
     (\$to \s* = \s* ").* (" \s* ; \s* )$
   }{$1$ENV{REPLACEMENT}$2}gx
 ' FILE

Вperl

  • вы можете использовать s{...}{...}flagsв дополнение к s/.../.../flags, что упрощает просмотр совпадающих пар (и позволяет {, }внутри, если они совпадают ).
  • с флагом x, вы можете добавить пробелы (и даже комментарии )внутри регулярного выражения, чтобы его было легче читать (обратите внимание, что эти пробелы не являются частью регулярного выражения, это \s*] соответствует любому количеству пробелов ).
  • можно смело использовать в замене любую произвольную строку, даже содержащую &, /,обратную косую черту или новую строку, передав их, например, через переменную среды.
  • пока вы не используете параметры -C/ -Mlocale/ -Mopen=locale..., perlработает на уровне байтов, поэтому его .*всегда будет соответствовать, даже если ввод не не формирует допустимый текст в локали.
  • В отличие от некоторых sedреализаций, perlне имеет ограничения на длину строки (, кроме доступной памяти ), и не будет подавляться при вводе, содержащем байты NUL или не заканчивающемся символом новой строки.

Чтобы разрешить пробелы и в замещающей части, вы можете добавить флаг e, который приводит к замене кода perl:

REPLACEMENT="$new_email_address" perl -pi -e '
  s{
     (\$to \s* = \s* ").* (" \s* ; \s* )$
   }{
     $1. $ENV{REPLACEMENT}. $2
   }gxe
 ' FILE

Например. Также помните, что использование readбез установки $IFSи без -rредко имеет смысл.

4
18.03.2021, 22:25

Один из способов разделить кавычки состоит в том, чтобы разорвать команду sed, воспользовавшись помощью нескольких -кодлеток sed для поиска и замены.

q=\"; # a double quote character
sed -i \
    -e '/$to = ".*";$/c\'  \
    -e "\$to = $q$new_email_address$q;" \
FILE

sed -i \
    -e '/$to = ".*";$/!b' \
    -e "s//\$to = $q$new_email_address$q;/" \
FILE;
0
18.03.2021, 22:25

Цитируемая команда чем-то похожа на то, что я написал в недавнем ответе(с добавлением -i):

sed 's/$to = ".*";$/$to = "'"$new_email_address"'";/' file

Это вызов sedс помощью одной команды редактирования. Команда, используемая в выражении sed, — это команда s, которая выполняет подстановку, то есть заменяет что-то, что соответствует регулярному выражению, чем-то другим.

Общая форма команды srange s/pattern/replacement/flags. В команде, с которой мы здесь имеем дело, нет rangeвыражения (, команда sприменяется ко всем строкам во входном тексте ), и нет flags. Таким образом, у нас есть sedсценарий редактирования общей формы

s/pattern/replacement/

Как видно из команды, бит patternравен

$to = ".*";$

Этот шаблон соответствует буквальному тексту $to = ", за которым следует что угодно(любая длина последовательности любого символа ), за которой следует буквальный текст ";. $в конце заставляет последний бит ";соответствовать самому концу строки.

Затем у нас есть бит replacement.

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

$to = "

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

После вставки значения мы заканчиваем часть replacementкоманды sна

";

и это полное поле replacement,$to = ", за которым следует некоторое значение (, новый адрес электронной почты ), а затем ";.

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

Структура команды sed:

    sed 's/$to = ".*";$/$to = "'"$new_email_address"'";/' file
         s/  pattern   /          replacement          /

Биты строк, составляющих выражение sedв оболочке:

    sed 's/$to = ".*";$/$to = "'"$new_email_address"'";/' file
         ^^^^^^^^^^^^^^^^^^^^^^  ^^^^^^^^^^^^^^^^^^  ^^^
         single-quoted string    double-quoted       final single-quoted bit
                                 string for shell
                                 variable expansion

Вышеизложенное в более схематичном виде

    sed 'something here'"$variable_value_here"'ending here' file

Бит something hereзаканчивается двойной кавычкой, а бит ending hereначинается двойной кавычкой.

0
18.03.2021, 22:25

Еще один способ может быть:

  • Сначала сделайте адрес электронной почты безопасным для подключения к правой стороне команды sed s///.
  • Затем напишите многострочный, разбросанный пробелами код sed в терминах формата printf.
  • Наконец, соберите код sed, удалите из него все пробелы и передайте его опции sed -f.
email_rhs=$(printf '%s\n' "$new_email_address" | sed -e 's:[\&/]:\\&:g;$!s:$:\\:')

sed -i -Ef - <<_CODE_ FILE_PATH
$(printf -v fmt \
  '
    s/
      (\$to \s* = \s*) ".*" ;$
     /
      \\\\1 "%%s" ;
    /g
  '
printf "${fmt//[$IFS]/}" "$email_rhs")
_CODE_
0
18.03.2021, 22:25

Теги

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