Обратные слеши усложняют жизнь
Жизнь всегда становится сложной, когда у вас есть оболочка, и sed
оба пытаются интерпретировать обратную косую черту. На самом деле, это становится очень больно.
Давайте рассмотрим простой случай: вы хотите, чтобы sed
заменил первую обратную косую черту в строке знаком at:
sed 's/\\/@/' file
Обратите внимание, что нам нужно было указать две обратные косые черты здесь, и поскольку команда была в одинарных кавычках, оболочка не выполняла для нас никакой интерпретации & mdash; слава богу!
Теперь давайте предположим, что нам нужно использовать двойные кавычки вместо одинарных. Теперь нам нужно набрать:
sed "s/\\\\/@/" file
Почему? Потому что оболочка сначала сканирует строку и видит обратную косую черту и переводит ее в одну обратную косую черту; и затем он видит второй обратный слеш-обратный слеш и переводит его в другой одиночный обратный слеш. Итак, sed
увидит две обратные косые черты и сделает то, что вы хотели.
bash-as-bash ведет себя не так, как bash-as-sh
У вас может быть /bin/echo
на вашем компьютере (у меня на моем компьютере Mac OS X 10.7.3), который ведет себя не так, как встроенный в bash
echo
. Я могу получить следующий вывод (от /bin/bash
):
$ echo '\\' | sed "s/\\\\/@/"
@\
$ /bin/echo '\\' | sed "s/\\\\/@/"
@\
$ echo '\\' | sed 's/\\\\/@/'
@
$ /bin/echo '\\' | sed 's/\\\\/@/'
@
$
Давайте добавим еще один гаечный ключ в работу (у меня болит мозг - как у вас дела?). Я получаю другой ответ от /bin/sh
(который очень похож, но не идентичен /bin/bash
; разница составляет 64 байта на диске, и оба bash
версии 3.2):
$ echo '\\' | sed "s/\\\\/@/"
@
$ /bin/echo '\\' | sed "s/\\\\/@/"
@\
$ echo '\\' | sed 's/\\\\/@/'
\
$ /bin/echo '\\' | sed 's/\\\\/@/'
@
$
Да, есть разница. А теперь я в замешательстве!
В своем обсуждении вы упоминаете использование:
replacement=$(echo "\\\macro{some text}")
Это усложняет жизнь. У вас есть оболочка, обрабатывающая этот текст, и встроенная в bash echo
также обрабатывает текст. И если вы используете bash
в качестве sh
, вы получите один ответ, а если вы используете bash
в качестве bash
, вы получите другой ответ.
Когда я тестировал bash
как sh
, я обнаружил:
replacement="\\\\macro{some text}" # 4 slashes, not 3
Это получает два обратных слэша в $replacement
. Вы можете продемонстрировать это, запустив:
$ /bin/echo "$replacement"
\\macro{some text}
$ echo "$replacement"
\macro{some text}
$
Так полезно ... чтобы получить $(echo ...)
для получения двух обратных косых черт в выходных данных при последующем echo
этом, вам нужно не менее 16 (!) Обратных косых черт в строке:
$ replacement=$(echo "\\\\\\\\\\\\\\\\macro{some text}")
$ echo "$replacement"
\\macro{some text}
$
Осторожно : это относится к bash-as-sh; запустив bash-as-bash, вы получите:
$ replacement=$(echo "\\\\\\\\\\\\\\\\macro{some text}")
$ echo "$replacement"
\\\\\\\\macro{some text
$
Разве жизнь не веселая! Таким образом, чтобы это сработало, вам придется довольно тщательно предварительно обработать шаблон поиска и шаблоны замены, чтобы получить достаточное количество обратной косой черты в каждом из них. Как много? Тьфу ...
Тестовый скрипт
Вот тестовый скрипт, который я назвал, с потрясающей оригинальностью и ясностью имени, xx
:
#!/bin/bash
echo "happy /word/ today" > file
word="/word/"
/bin/echo "word=<<$word>>"
replacement='\\\\macro{some text}'
/bin/echo "repl=<<$replacement>>"
echo "repl=<<$replacement>>"
/bin/echo sed -e "s#$word#$replacement#" file
sed -e "s#$word#$replacement#" file
echo
replacement="\\\\macro{some text}"
/bin/echo "repl=<<$replacement>>"
echo "repl=<<$replacement>>"
/bin/echo sed -e "s#$word#$replacement#" file
sed -e "s#$word#$replacement#" file
echo
replacement=$(echo "\\\\\\\\macro{some text}")
/bin/echo "$replacement"
echo "$replacement"
echo
replacement=$(echo "\\\\\\\\\\\\\\\\macro{some text}")
/bin/echo "$replacement"
echo "$replacement"
Тестовый скрипт, запускаемый bash-as-bash
Это вывод из bash xx
:
$ bash xx
word=<</word/>>
repl=<<\\\\macro{some text}>>
repl=<<\\\\macro{some text}>>
sed -e s#/word/#\\\\macro{some text}# file
happy \\macro{some text} today
repl=<<\\macro{some text}>>
repl=<<\\macro{some text}>>
sed -e s#/word/#\\macro{some text}# file
happy \macro{some text} today
\\\\macro{some text}
\\\\macro{some text}
\\\\\\\\macro{some text}
\\\\\\\\macro{some text}
$
Тестовый скрипт, запускаемый bash-as-sh
И это вывод из sh xx
:
$ sh xx
word=<</word/>>
repl=<<\\\\macro{some text}>>
repl=<<\\macro{some text}>>
sed -e s#/word/#\\\\macro{some text}# file
happy \\macro{some text} today
repl=<<\\macro{some text}>>
repl=<<\macro{some text}>>
sed -e s#/word/#\\macro{some text}# file
happy \macro{some text} today
\\macro{some text}
\macro{some text}
\\\\macro{some text}
\\macro{some text}
$
Где-то среди всего, что является (большей частью) ответом на вашу проблему. Сказать, что я был удивлен, обнаружив, что такая большая разница между bash-as-bash и bash-as-sh, не начинает охватывать территорию.