用sed引用特殊字符

时间:2013-12-06 15:12:51

标签: unix sed

我正在尝试查看传递给我的程序的变量(变量是1美元)并用所述特殊字符的引用形式替换任何特殊字符,以便没有特殊字符实际上做它通常会做的事情

我的代码是

#!/bin/sh
target="$1"
newtarget=`echo "$target" | sed -e s/\*/\\*/g`
newtarget=`echo "$newtarget" | sed -e s/\^/\\^/g`
newtarget=`echo "$newtarget" | sed -e s/\+/\\+/g`
newtarget=`echo "$newtarget" | sed -e s/\-/\\-/g`
newtarget=`echo "$newtarget" | sed -e s/\\/\\\/g`
newtarget=`echo "$newtarget" | sed -e s/\./\\./g`
newtarget=`echo "$newtarget" | sed -e s/\$/\\$/g`
newtarget=`echo "$newtarget" | sed -e s/\[/\\[/g`
newtarget=`echo "$newtarget" | sed -e s/\]/\\]/g`
sed s/"$newtarget"/"$2"/g "$3" > "$3.updated"
mv "$3.updated" $3

我的第一行,带有$ target,应该查看目标字符串,看看字符串中是否有*。如果有,它将用*替换它。在代码中,它显示为*然后是\ *,是因为程序没有看到*并且认为它想要实际使用*,它只是通过引用*将*视为常规字符。我在所有其他行中都做了同样的事情,但是使用了不同的字符。在第一个之后,它应该检查newtarget并执行相同的操作,但具有不同的字符。

我的整体程序应该做的是,它传递了3个参数,第一个是要替换的字符串,第二个是替换它的字符串,第三个是文件名。所以到最后,如果文件最初是像

那样
aa\^a*aa$aa[aaa$a]a 

我提供

"a\^a*" "test"

作为参数,结果应为

atestaa$aa[aaa$a]a 

但是我的代码仍然不起作用。我的代码出了什么问题?我不知道我的sed语法是否正确编码,或者我的附加语句是否不起作用,或者我是否需要特殊引用一些特殊字符。

编辑:我知道我应该能够像我一样使用多个sed命令来做这个,但我不知道他们为什么不能正常工作,所以我很确定这与我的引用有关在“newtarget =”行末尾的实际sed命令中。

EDIT2:我现在在我的代码中引用了我的sed参数,但它仍然无法正常工作。我需要特殊的方式来引用某些特殊字符吗?我假设在每个角色前加一个反斜杠就可以正确引用它。

#!/bin/sh
target="$1"
newtarget=`echo "$target" | sed -e 's/\*/\\*/g'`
newtarget=`echo "$newtarget" | sed -e 's/\^/\\^/g'`
newtarget=`echo "$newtarget" | sed -e 's/\+/\\+/g'`
newtarget=`echo "$newtarget" | sed -e 's/\-/\\-/g'`
newtarget=`echo "$newtarget" | sed -e 's/\\/\\\/g'`
newtarget=`echo "$newtarget" | sed -e 's/\./\\./g'`
newtarget=`echo "$newtarget" | sed -e 's/\$/\\$/g'`
newtarget=`echo "$newtarget" | sed -e 's/\[/\\[/g'`
newtarget=`echo "$newtarget" | sed -e 's/\]/\\]/g'`
sed s/"$newtarget"/"$2"/g "$3" > "$3.updated"
mv "$3.updated" $3

2 个答案:

答案 0 :(得分:3)

sed的多次调用的目的是在每次出现一组字符之前放置一个文字后挡板。这可以通过一次调用sed来完成,但您需要注意如何指定集合。

首先,让我们看看一般命令的样子:

newtarget=$( echo "$target" | sed -e 's/\([...]\)/\\\1/g'

其中...将替换为要转义的字符集。此命令使用括号捕获其中一个字符的单个实例,将其替换为后挡板,后跟捕获的字符。要指定字符集,请使用

[]*^+\.$[-]

两个注意事项:首先,]必须先出现,以免被误认为是集合的结尾,因为[]是无效的集合。其次,-必须是最后一个,因此它不会被误认为是范围运算符(例如,[a-z]是一组小写字母,但[az-]只是三个字符{{ 1}},az)。

全部放在一起:

-

答案 1 :(得分:2)

您所做的问题是您没有引用sed表达式。例如,写

sed s/\*/\\*/

sed 's/\*/\\*/'

sed s/\*/\\\\*/

我不确定为什么你需要那个复杂的功能来逃避特殊字符。您可以定义一个函数来返回转义的输入字符串:

myescape() { printf "%q" "$1"; }
  

%q

     

导致printf以格式输出相应的参数   可以重用为shell输入。

将参数传递给sed的另一个函数:

myreplace() { sed "s/$1/$2/" <<< "$3"; }

现在你可以通过说:

来调用它
myreplace "$(myescape 'pattern')" "replacement" "original_string"

示例:

$ myescape() { printf "%q" "$1"; }
$ myreplace() { sed "s/$1/$2/" <<< "$3"; }
$ myreplace $(myescape 'a\^a*') 'test' 'aa\^a*aa[aaa]a'
atestaa[aaa]a