当替换包含反斜杠时,如何查找和替换文件中BASH变量的第一个匹配?

Vil*_*age 1 bash replace symbols sed

我正在尝试使用sed和BASH变量进行查找和替换.由于替换的内容包含许多/字符,我使用的#代替/,通常用于sed:

 sed -i "0,\#^$word#s##$replacement#" ./file
Run Code Online (Sandbox Code Playgroud)

我要替换的项目包含\符号:

 replacement=$(echo "\macro{30\percent of bears eat fish.}")
Run Code Online (Sandbox Code Playgroud)

但是,当我进行更换时,\总会消失.

  • 我无法控制其中的内容 "\macro{30\percent of bears eat fish.}"
  • 如果\t出现,它被解释为缩进,但我只是希望它\t在输出中打印.

我怎样才能做一个查找和替换为sed的时候\,/或其他符号可能替换文本中找到?

Jon*_*ler 5

反斜杠使生活变得复杂

当你拥有shell并且sed都试图解释反斜杠时,生活总是变得棘手.事实上,它真的很痛苦.

我们来看一个简单的例子:你想sed用一个符号替换一行上的第一个反斜杠:

sed 's/\\/@/' file
Run Code Online (Sandbox Code Playgroud)

请注意,我们需要在这里指定两个反斜杠,并且因为命令是单引号,所以shell没有为我们做任何解释 - 谢天谢地!

现在,让我们假设我们需要使用双引号而不是单引号.现在我们需要输入:

sed "s/\\\\/@/" file
Run Code Online (Sandbox Code Playgroud)

为什么?因为shell首先扫描字符串,并且它看到反斜杠反斜杠并将其转换为单个反斜杠; 然后它会看到第二个反斜杠反斜杠并将其转换为另一个反斜杠.那么,然后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/\\\\/@/'
@
$ 
Run Code Online (Sandbox Code Playgroud)

让我们在工作中抛出另一个扳手(我的大脑受伤了 - 你的应对怎么样?).我得到了一个不同的答案/bin/sh(这是非常相似但不完全相同/bin/bash;磁盘上的差异是64字节,两者都是bash版本3.2):

$ echo '\\' | sed "s/\\\\/@/"
@
$ /bin/echo '\\' | sed "s/\\\\/@/"
@\
$ echo '\\' | sed 's/\\\\/@/'
\
$ /bin/echo '\\' | sed 's/\\\\/@/'
@
$
Run Code Online (Sandbox Code Playgroud)

是的,有区别.而现在我很困惑!


在您的讨论中,您提到使用:

replacement=$(echo "\\\macro{some text}")
Run Code Online (Sandbox Code Playgroud)

这使生活变得复杂.你有shell处理文本,而bash的内置echo也处理文本.如果你使用,bashsh你得到一个答案,如果你使用,bash因为bash你得到另一个答案.

当我用bashas 测试时sh,我发现:

replacement="\\\\macro{some text}"   # 4 slashes, not 3
Run Code Online (Sandbox Code Playgroud)

这会得到两个反斜杠$replacement.你可以通过运行来证明:

$ /bin/echo "$replacement"
\\macro{some text}
$ echo "$replacement"
\macro{some text}
$
Run Code Online (Sandbox Code Playgroud)

非常有用......所以,为了让$(echo ...)你在随后的输出中产生两个反斜杠echo,你需要在字符串中不少于16个(!)反斜杠:

$ replacement=$(echo "\\\\\\\\\\\\\\\\macro{some text}")
$ echo "$replacement"
\\macro{some text}
$
Run Code Online (Sandbox Code Playgroud)

注意:这适用于bash-as-sh; 运行bash-as-bash,你得到:

$ replacement=$(echo "\\\\\\\\\\\\\\\\macro{some text}")
$ echo "$replacement"
\\\\\\\\macro{some text
$
Run Code Online (Sandbox Code Playgroud)

生活不是很有趣!因此,要使其工作,您将不得不对搜索模式和替换模式进行预处理,以便在每个模式中获得足够的反斜杠.多少?啊...


测试脚本

这是一个我称之为测试脚本,具有令人惊叹的原创性和明确的名称,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"
Run Code Online (Sandbox Code Playgroud)

测试脚本由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}
$ 
Run Code Online (Sandbox Code Playgroud)

测试脚本由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}
$ 
Run Code Online (Sandbox Code Playgroud)

在所有这些(大部分)问题的答案中.要说我很惊讶地发现bash-as-bash和bash-as-sh之间的这种差异并没有开始覆盖领土.