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

时间:2012-03-20 07:54:52

标签: bash sed symbols replace

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

 sed -i "0,\#^$word#s##$replacement#" ./file

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

 replacement=$(echo "\macro{30\percent of bears eat fish.}")

然而,当我进行替换时,\总是消失。

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

如果在替换文字中找到sed\或其他符号,如何使用/进行查找和替换?

6 个答案:

答案 0 :(得分:5)

反斜杠使生活变得复杂

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

让我们举个简单的例子:你希望sed用一个符号替换一行上的第一个反斜杠:

sed 's/\\/@/' file

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

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

sed "s/\\\\/@/" file

为什么呢?因为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/\\\\/@/'
@
$ 

让我们在工作中抛出另一个扳手(我的大脑受伤了 - 你的应对方式如何?)。我从/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}")

这使生活变得复杂。你有shell处理该文本,而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个(!)反斜杠:< / p>

$ 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之间的这种差异并没有开始覆盖领土。

答案 1 :(得分:4)

您的问题可能不是反斜杠。或者至少,你的一个问题。 : - )

在我系统上安装的sed中,您可以使用#作为替换的分隔符(即s#pattern#replacement#),但不能作为初始搜索的一部分。因此,你必须使用类似的东西:

 sed -i "1,/^$word/s##$replacement#" ./file

查看我的结果:

[ghoti@pc ~]$ printf 'one\ntwo\nthree\nfour\nfive\n' | sed '1,/three/d'
four
five
[ghoti@pc ~]$ printf 'one\ntwo\nthree\nfour\nfive\n' | sed '1,#three#d'
sed: 1: "1,#three#d": expected context address
[ghoti@pc ~]$ 

我在FreeBSD,但我很确定GNU sed的工作方式是一样的。

但是,如果您尝试匹配的模式中存在不兼容的字符,则无法帮助您。您只需要确保$word是一个格式正确的正则表达式,可以在sed中使用。

至于反斜杠...如果上面的建议给你带来任何快乐,请尝试将替换模式中的任何反斜杠加倍。但是,一次只处理一个问题,并从更简单的$replacement开始,以确保您的基本sed符号正常运行。

<强>更新

另外,为了它的乐趣:

[ghoti@pc ~]$ word="word"
[ghoti@pc ~]$ replacement="\\\macro{some text};"
[ghoti@pc ~]$ printf "Replace a word with some text.\n" | sed "s#$word#$replacement#"
Replace a \macro{some text}; with some text.
[ghoti@pc ~]$ replacement="\\\\macro{some text};"
[ghoti@pc ~]$ printf "Replace a word with some text.\n" | sed "s#$word#$replacement#"
Replace a \macro{some text}; with some text.
[ghoti@pc ~]$ printf "Replace a word with some text.\n" | sed "s/$word/$replacement/"
Replace a \macro{some text}; with some text.
[ghoti@pc ~]$ word="two"
[ghoti@pc ~]$ printf "one\ntwo\nthree\n" | sed "s/^$word/$replacement/"
one
\macro{some text};
three
[ghoti@pc ~]$ 

答案 2 :(得分:2)

这可能对您有用:

sed -i "0,\#^$word#s##$(printf '%q' $replacement)#" ./file

答案 3 :(得分:2)

word=$(echo -E "wo\rd")
replacement=$(echo -E "r/e\p")

echo -ne "a\nb\nc\nd\nwo\\\\rd\ne\nf\n" | sed -e "0,\#^${word//\\/\\\\}# s##${replacement//\\/\\\\}#"

请注意,您仍会遇到其他角色问题,&amp;和#替换字符串,当然还有单词字符串中的所有有效正则表达式字符

以下处理正则表达式字符

word=$(echo -E "^\$.*[#]wo\rd" | sed 's:[]\[\^\$\.\*#\\]:\\&:g')
replacement=$(echo -E "&#r/e\p" | sed 's:[&#\\]:\\&:g')

echo -ne "a\nb\nc\nd\n^\$.*[#]wo\\\\rd\ne\nf\n" | sed -e "0,\#^${word}# s##${replacement}#"

答案 4 :(得分:1)

试试这个(如果我理解你的问题):

R="macro{some text}"
sed -i "0,\#^$word#s##\\$R#" ./file

答案 5 :(得分:1)

试试这个

输入

/home/Scripts/dummyrun 

Unix命令

$> sed -i "s%/home/Scripts/dummyrun%Newpath%g" Input

<强>输出

打开输入文件,更改将自动反映。

 $> cat Input 
    Newpath

上面的命令将/ home / Scripts / dummyrun替换为输入文件中的Newpath。