如何将包含斜杠的变量传递给sed


100

如何将包含斜杠的变量作为模式传递给sed

例如,如果我有以下变量:

var="/Users/Documents/name/file"

我想这样传递sed

sed "s/$var/replace/g" "$file"

但是我得到了错误。我该如何解决这个问题?

Answers:


193

使用替代的正则表达式定界符,因为sed您可以使用任何定界符(包括控制字符):

sed "s~$var~replace~g" $file

2
不起作用 我尝试sed -i“ s〜blah〜$ var〜g file”并得到:“ sed -e表达式#1,字符70:未终止的s命令”。我已经确认罪魁祸首是$ var中的斜线。这种解决方案的变种无处不在,但都不起作用。sed最近更新了吗?我在Ubuntu 14.04.4 LTS上的v4.2.2上。
Paul Ericson'9

这取决于$var
anubhava's

1
与@PaulEricson观察到的〜不兼容,但仍与|
肯尼·何

1
最后一个“〜”(后G)似乎并没有被需要,至少在AWS亚马逊的Linux(CentOS的基础)
萨乌尔·马丁内斯Vidals

1
您保存了我的一天
user7131571

61

一个纯bash的答案:使用参数扩展来反斜杠转义变量中的任何斜杠:

var="/Users/Documents/name/file"
sed "s/${var//\//\\/}/replace/g" $file

1
这是一个好方法,因为您并不总是知道哪些字符包含变量。因此,如有疑问,您始终可以逃脱sed分隔符。例如:sed“ s:$ {var //:/ \\ ::: replace:g” $ file
Stephane L

美丽。使我的一些重命名脚本更加整洁。

今天学到的东西很美,谢谢。应该是公认的答案。
史蒂夫·凯莱特

6
有关此处使用的模式的一些说明:${parameter/pattern/string}。因此,在这种情况下,参数为var,模式为/\/,字符串为\\/。模式的所有实例都将替换,因为模式以开头/
乔什(Josh)'18年

1
@josh,因此模式的前导斜杠实际上并不是要替换的模式的一部分。
格伦·杰克曼

32

尽管比anubhava的答案难看,但另一种方法是通过var使用另一个sed命令转义所有反斜杠:

var=$(echo "$var" | sed 's/\//\\\//g')

然后,这将起作用:

sed "s/$var/replace/g" $file

12

使用/在sed与变量中的斜杠分隔符的意志发生冲突时取代,因此你会得到一个错误。规避此问题的一种方法是使用另一个分隔符,该分隔符对于该变量中的任何字符都是唯一的。

var="/Users/Documents/name/file"

您可以使用适合情况的八字形字符(或其他任何不/便于使用的字符)

sed "s#$var#replace#g" 

要么

sed 's#$'$var'#replace#g'

当变量不包含空格时,这是合适的

要么

sed 's#$"'$var'"#replace#g'

使用上面的方法是比较明智​​的,因为与只对整个命令加双引号相比,我们有兴趣只用该变量中的任何内容来代替,因为整个命令可能会使您的shell插入可能被视为shell特殊shell字符的任何字符。


不起作用 我尝试使用sed -i“ s〜blah〜$ var〜g文件”并得到:“ sed -e表达式#1,字符70:未终止的s命令”。我已经确认罪魁祸首是$ var中的斜线。这种解决方案的变种无处不在,而且都不起作用。sed最近更新了吗?我在Ubuntu 14.04.4 LTS上的v4.2.2上。
Paul Ericson

@PaulEricson我无法在任何可用的Ubuntu上重现此问题。如果$var包含~,显然您将需要选择其他定界符。“未终止”错误听起来像您$var包含换行符;您可以通过在值中的每个换行符后加反斜杠来解决此问题,但我认为这不是完全可移植的。基本上,这与价值的削减问题无关。
Tripleee

@PaulEricson哦,您的报价不正确,您不应在引号内包含文件名。sed -i "s~blah~$var~g" file仅在实际sed脚本周围加上引号。
Tripleee

5

这是一个古老的问题,但是这里没有任何答案s/from/to/会详细讨论操作。

sed声明的一般形式是

*address* *action*

其中address可以是正则表达式范围或行号范围(或为空,在这种情况下,该操作将应用于每个输入行)。所以举个例子

sed '1,4d' file

将删除第1至4行(地址是行号范围1,4,而动作ddelete命令);和

sed '/ick/,$s/foo/bar/' file

将在regex上的第一个匹配项与文件末尾之间的任何行上foo用替换第bar一个匹配项ick地址是范围/ick/,$,而动作是replace s命令s/foo/bar/)。

在这种情况下,如果ick来自变量,则可以

sed "/$variable/,\$s/foo/bar/"

(请注意,请使用双引号而不是单引号,以便shell可以对变量进行插值,并且必须在双引号内使用文字美元符号进行引号),但是如果变量包含斜杠,则会出现语法错误。(shell扩展变量,然后将结果字符串传递给sed;,因此sed只能看到文字文本-它没有shell变量的概念。)

解决方法是使用其他定界符(显然,您需要能够使用变量值中不会出现的字符),但是与这种s%foo%bar%情况不同,如果要使用其他定界符,则还需要在定界符前加反斜杠比默认分隔符/

sed "\\%$variable%,\$s/foo/bar/" file

(在单引号内,单反斜杠显然就足够了);或者您可以分别转义值中的每个斜杠。此特定语法仅适用于Bash:

sed "/${variable//\//\\/}/,\$s/foo/bar/" file

或者,如果您使用其他外壳,请尝试

escaped=$(echo "$variable" | sed 's%/%\\/%g')
sed "s/$escaped/,\$s/foo/bar/" file

为了清楚起见,如果$variable包含字符串,1/2则上述命令将等效于

sed '\%1/2%,$s/foo/bar/' file

在第一种情况下,以及

sed '/1\/2/,$s/foo/bar/' file

在第二。


4

使用Perl,其中变量是一等公民,而不仅仅是扩展宏:

var=/Users/Documents/name/file perl -pe 's/\Q$ENV{var}/replace/g' $file
  • -p 逐行读取输入并在处理后打印该行
  • \Q引用以下字符串中的所有元字符(此处显示的值不需要,但如果包含的值[或某些其他常规表达式专用的值,则是必需的)

整个Stack Exchange上数十个“在sed表达式中使用变量”问题的唯一普遍有用的答案。其他所有内容实际上都是“逃避sed的特殊操作”,鉴于变量和sed的可变性,这实际上是无用的。
Heath Raftery
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.