sh脚本中的'sed'出现问题

时间:2013-12-05 23:39:12

标签: regex unix sed pipe sh

我正在尝试编写一个脚本来交换文件中的文本:

sed s/foo/bar/g myFile.txt > myFile.txt.updated
mv myFile.txt.updated myFile.txt

在shell中,我会唤起sed程序,它会在myFile.txt中交换文本并将更改后的文本行重定向到第二个文件。 mv然后将.updated txt文件移动到myFile.txt,覆盖它。

我需要注意“特殊字符”,但我正在使用正则表达式。

我写道:

#!/bin/sh
#First, I set up some more descriptive variables for my arguments    
initialString="$1"
shift
desiredChange="$1"
shift
document="$1"
#Then, I evoke sed on my document to change all 'special characters' into
#'/special charachters'
updatedDocumentText=`sed 's:[]\[\^\$\.\*\+\-\?\\\\/]:\\\\&:g' $document`
#below, I'm checking my work
echo $updatedDocumentText
#Now, I make that 'new string' the output of a program (echo) and pipe that
#output to sed
finalDocument=echo $updatedDocumentText | sed 's/$initialString/$desiredChange/g'
#Checking my work
echo $finalDocument
#Now this string has to be the output of a program so I can use the
# redirect operator. I'm using echo as the program again.
echo $finalDocument > $document

有两个问题。最重要的是:第二个sed认为字符串$ updatedDocumentText中的文本是文件的名称。我一直在研究这个问题的时间比经验丰富的程序员能够相信的要长,而且我已经完全掌握了这一点。上面的配置给了我所尝试的所有内容中最明显的错误。我在绳子的尽头,如果可以,请救救我。

第二个小问题是,我的正则表达式不会替换'\',但它适用于所有其他特殊字符。

2 个答案:

答案 0 :(得分:1)

我认为您忘记了特殊引号或$()语法:

finalDocument=$(echo $updatedDocumentText | sed "s/$initialString/$desiredChange/g")

要替换反斜杠,您必须将它们加倍,例如:

sed 's/\\/\\\\/g' infile

答案 1 :(得分:0)

您不需要中间文件,您可以使用-i标志来替换字符串:

sed -e s/foo/bar/g -i myFile.txt

不是将整个文件读入变量然后回显它,而是使用管道:修改输入并将输出传递给下一个命令。所以而不是:

updatedDocumentText=`sed 's:[]\[\^\$\.\*\+\-\?\\\\/]:\\\\&:g' $document`
finalDocument=echo $updatedDocumentText | sed 's/$initialString/$desiredChange/g'
echo $finalDocument > $document

这是“某种”等同物:

sed -e 's:[]\[\^\$\.\*\+\-\?\\\\/]:\\\\&:g' -e "s/$initialString/$desiredChange/g" -i "$document"

我说“有点”,因为你在代码中犯了其他一些错误:

  • sed 's/$initialString/$desiredChange/g'中你使用了单引号,所以变量没有在那里插值,所以这个命令很可能什么都没有替换。
  • echo $updatedDocumentText中,某些转义的特殊字符不会被保留,可以通过双引号修复:echo "$updatedDocumentText"

最后,我认为你真正想做的是逃避$initialString中的特殊字符,而不是文件内容。所以我认为这就是你要找的东西:

escaped=$(echo "$initialString" | sed -e 's/[].+-[$\\^*]/\\&/g')
sed -e "s/$escaped/$desiredChange/" -i "$document"

所有引用都很重要并且有用。 -e的{​​{1}}标志并不是必需的,但我总是希望它们能够完全清楚它是表达式,而不是文件名参数。

关于替换特殊字符,this other answer也可能对您有用。