Как использовать sed для замены строк, в которых есть "\"? [duplicate]

ksh93 и zsh имеют поддержку обратной ссылки (точнее 1, ссылки на группы захвата в замене) внутри ${var/pattern/ замена}, а не баш.

ksh93:

$ var='Blah: -> r1-ae0-2 / [123]'
$ printf '%s\n' "${var/*@(->*([[:space:]])+([^[:space:]]))*/\1}"
-> r1-ae0-2

zsh:

$ var='Blah: -> r1-ae0-2 / [123]'
$ set -o extendedglob
$ printf '%s\n' "${var/(#b)*(->[[:space:]]#[^[:space:]]##)*/$match[1]}"
-> r1-ae0-2

(mksh справочная страница также упоминает, что будущие версии будут поддерживать его с ${KSH_MATCH[1]} для первой группы захвата. По состоянию на 25 апреля 2017 г. еще недоступно).

Однако с помощью bash вы можете сделать следующее:

$ [[ $var =~ -\>[[:space:]]*[^[:space:]]+ ]] &&
  printf '%s\n' "${BASH_REMATCH[0]}"
-> r1-ae0-2

Что лучше, так как проверяется, что шаблон найден первым.

Если регулярные выражения вашей системы поддерживают \s/\S, вы также можете сделать:

re='->\s*\S+'
[[ $var =~ $re ]]

С помощью zsh вы можете получить всю мощь PCRE с:

$ set -o rematchpcre
$ [[ $var =~ '->\s*\S+' ]] && printf '%s\n' $MATCH
-> r1-ae0-2

С zsh -o extendedglob, см. также:

$ printf '%s\n' ${(SM)var##-\>[[:space:]]#[^[:space:]]##}
-> r1-ae0-2

Переносимо:

$ expr " $var" : '.*\(->[[:space:]]*[^[:space:]]\{1,\}\)'
-> r1-ae0-2

Если шаблон встречается в строке несколько раз, поведение будет различаться для всех этих решений. Однако ни один из них не даст вам список всех совпадений, разделенных новой строкой, как в вашем решении на основе GNU-grep.

Чтобы сделать это, вам нужно сделать цикл вручную. Например, с bash:

re='(->\s*\S+)(.*)'
while [[ $var =~ $re ]]; do
  printf '%s\n' "${BASH_REMATCH[1]}"
  var=${BASH_REMATCH[2]}
done

С zsh вы можете прибегнуть к такому трюку, чтобы сохранить все совпадения в массиве:

set -o extendedglob
matches=() n=0
: ${var//(#m)->[[:space:]]#[^[:space:]]##/${matches[++n]::=$MATCH}}
printf '%s\n' $matches

1 back- ссылки чаще обозначают шаблон, который ссылается на то, что было сопоставлено более ранней группой. Например, базовое регулярное выражение \(.\)\1 соответствует одному символу, за которым следует тот же символ (оно соответствует aa, а не ab ).Это \1 является обратной ссылкой на эту группу захвата \(.\) в том же шаблоне.

ksh93 поддерживает обратные ссылки в своих шаблонах (например, ls -d -- @(?)\1 перечислит имена файлов, состоящие из двух одинаковых символов), а не другие снаряды. Стандартные BRE и PCRE поддерживают обратные ссылки, но не стандартную ERE, хотя некоторые реализации ERE поддерживают ее как расширение. bash [[ foo =~ re ]] использует ERE.

[[ aa =~ (.)\1 ]]

не будет соответствовать, но

re='(.)\1'; [[ aa =~ $re ]]

может, если ERE системы поддерживают это.

1
27.09.2016, 15:50
3 ответа
$ cat MyFile.txt 
Username=myusername
EncryptedPassword=9k3S+sS3g\=\=
$ oldusername="myusername"
$ oldpassword="9k3S+sS3g\=\="
$ newusername="mynewusername"
$ newpassword="U8djlk+h\=="

$ perl -pe "s/\Q$oldusername/q($newusername)/e ; s/\Q$oldpassword/q($newpassword)/e" MyFile.txt
Username=mynewusername
EncryptedPassword=U8djlk+h\==

From perldoc для \ Q и q ()

Возвращает значение EXPR со всеми символами ASCII, не являющимися "словами" , с обратной косой чертой (то есть, все символы ASCII не совпадению / [A-Za-z_0-9] / будет предшествовать обратная косая черта в возвращаемой строке, независимо от настроек локали.) Это внутренняя функция реализация экранирования \ Q в строках, заключенных в двойные кавычки

Если результат удовлетворительный, используйте perl -i -pe для редактирования на месте, так же, как sed

0
28.01.2020, 02:12

Глупый обходной путь, избегайте всех обратных косых черт:

oldpassword="$(printf "$oldpassword" | sed 's/\\/\\\\/g')"
sed -i "s/$oldpassword/$newpassword/g" MyFile.txt
0
28.01.2020, 02:12

Вы сделали эхо с содержимым $oldpassword? Обратные слеши будут отсутствовать в $oldpassword,

$ oldpassword=9k3S+sS3g\=\=
$ echo $oldpassword
9k3S+sS3g==

поэтому совпадение будет неудачным.

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

$ oldpassword='9k3S+sS3g\=\=
-1
28.01.2020, 02:12

Теги

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