转义sed bash脚本变量中的问号字符

时间:2014-07-06 19:57:57

标签: regex bash sed

我有一组已保存的html文件,其中包含http://mywebsite.com/showfile.cgi?key=somenumber形式的链接,但我想要删除问号(侧面故事是firefox讨厌的?并随机将其转换为%3F我是确定有一些神奇的解决办法,但那是另一个问题...)

但是,我认为我的代码导致在通过bash将选项存储为变量时无法正确读取/保存/处理问号字符

# Doesn't work (no pattern matched)
SED_OPTIONS='-i s/\.cgi\?key/\.cgikey/g'

# Works e.g. http://mywebsite.com/showfileblah?key=somenumber
SED_OPTIONS='-i s/\.cgi/blah/g'

# Leaves question mark in e.g. http://mywebsite.com/showfile.blah?key=somenumber
SED_OPTIONS='-i s/cgi\?/blah/g'

# Actual sed command run when using SED_OPTIONS (I define FILES earlier in
# the code)
sed $SED_OPTIONS $FILES

# Not using the SED_OPTIONS variable works
# e.g. http://mywebsite.com/showfile.cgikey=somenumber
sed -i s/\.cgi\?key/\.cgikey/g $FILES

如何使用SED_OPTIONS变量获取完整命令?

2 个答案:

答案 0 :(得分:3)

在变量中存储选项和参数列表的最安全的方法是使用数组

此外:

  • 您正在使用基本正则表达式(无-r-E选项),因此?不是特殊字符。并且不需要逃避。
  • 替换字符串中,不是正则表达式,请勿转义.
  • 无需选项g,因为您只是每行替换 1 次。
# Create array with individual options/arguments.
SED_ARGS=( '-i' 's/\.cgi?key/.cgikey/' )

# Invoke `sed` with array - note the double-quoting.
sed "${SED_ARGS[@]}" $FILES

同样,将数组用于输入文件列表会更安全。 $FILES仅在单个文件名不包含嵌入的空格或其他受shell扩展限制的元素时才有效。

一般而言:

  • 单个 - 引用字符串文字,例如此处的sed脚本 - 以防止shell解释它们。
  • Double -quote 变量引用,以防止shell对它们执行其他操作,例如路径名扩展(globbing)和单词拆分(通过以下方式拆分为多个令牌)空白)。

答案 1 :(得分:2)

我建议将sed的参数存储在数组中:

SED_OPTIONS=( '-i' '-e' 's/\.cgi?key/\.cgikey/g' )

sed "${SED_OPTIONS[@]}" $FILES

但是,这只是问题的一部分。

首先,请注意当您输入:

sed -i s/\.cgi\?key/\.cgikey/g $FILES

sed看到的脚本参数实际上是:

s/.cgi?key/.cgikey/g

因为您没有使用任何引号来保留反斜杠。 (为了演示,使用printf "%s\n" s/\.cgi\?key/\.cgikey/g,从而避免了echo是否正在解释反斜杠的任何问题。)这样做的一个副作用是:例如:

http://example.com/nodotcgi?key=value

将映射到:

http://example.com/nodo.cgikey=value

在设置SED_OPTIONS时使用单引号可确保在需要时保留反斜杠,而不是在?工作之前添加反斜杠。我的Mac上有GNU sed和BSD sed;为清楚起见,我将它们设为gnu-sedbsd-sed。请注意,BSD sed需要-i的后缀,并且不会接受-i的标准输入。所以,我从命令中删除了-i

$ URLS=(http://example.com/script.cgi?key=value http://example.com/nodotcgi?key=value)
$ SED_OPTIONS=( '-e' 's/\.cgi?key/\.cgikey/g' )
$ printf "%s\n" "${URLS[@]}" | bsd-sed "${SED_OPTIONS[@]}"
http://example.com/script.cgikey=value
http://example.com/nodotcgi?key=value
$ printf "%s\n" "${URLS[@]}" | gnu-sed "${SED_OPTIONS[@]}"
http://example.com/script.cgikey=value
http://example.com/nodotcgi?key=value
$ SED_OPTIONS=( '-e' 's/\.cgi\?key/\.cgikey/g' )
$ printf "%s\n" "${URLS[@]}" | bsd-sed "${SED_OPTIONS[@]}"
http://example.com/script.cgikey=value
http://example.com/nodotcgi?key=value
$ printf "%s\n" "${URLS[@]}" | gnu-sed "${SED_OPTIONS[@]}"
http://example.com/script.cgi?key=value
http://example.com/nodotcgi?key=value
$

注意sed的两个版本在问号前面有反斜杠时的行为差异(示例的第二部分)。