简体   繁体   English

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

[英]Escaping question mark character in sed bash script variable

I have a set of saved html files with links in them of the form http://mywebsite.com/showfile.cgi?key=somenumber but I want to kill the question mark (side-story is that firefox hates ? and randomly converts it to %3F I'm sure there's some magic fix but that's for another question...) 我有一组保存的html文件,它们中的链接的格式为http://mywebsite.com/showfile.cgi?key=somenumber,但我想取消问号(故事是Firefox讨厌?并随机转换它到%3F,我确定有一些魔术修复方法,但这是另一个问题……)

However, I think my code is causing the question-mark character to not be read/saved/handled properly when storing the options as a variable by bash 但是,我认为当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

How can I get the full command to work using the SED_OPTIONS variable? 如何使用SED_OPTIONS变量使完整命令起作用?

The safest way to store a list of options and arguments in variables is to use an array : 在变量中存储选项和参数列表最安全方法是使用数组

Also: 也:

  • You're using a basic regular expression (no -r or -E option), so ? 您正在使用基本的正则表达式(没有-r-E选项),所以? is not a special char. 不是特殊字符。 and needs no escaping. 并且不需要逃避。
  • In the replacement string , which is not a regex, do not escape . 在不是正则表达式的替换字符串中 ,请勿转义. .
  • No need for option g , since you're only replacing 1 occurrence per line. 不需要选项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

Similarly, it would be safer to use an array for the list of input files. 同样,将数组用于输入文件列表会更安全。 $FILES will only work if the individual filenames contain no embedded whitespace or other elements subject to shell expansions. 仅当单个文件名不包含嵌入式空格或其他受Shell扩展限制的元素时, $FILES才起作用。

Generally: 通常:

  • Single -quote string literals , such as the sed script here - to prevent the shell from interpreting them. 引号字符串文字 ,例如此处的sed脚本-防止shell解释它们。
  • Double -quote variable references , to prevent the shell from performing additional operations on them, such as pathname expansion (globbing) and word splitting (splitting into multiple tokens by whitespace). 引号变量引用 ,以防止外壳程序对其执行其他操作,例如路径名扩展(globbing)和字词拆分(由空格拆分为多个标记)。

I suggest storing the arguments for sed in an array: 我建议将sed的参数存储在数组中:

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

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

However, that's only a part of the trouble. 但是,那只是麻烦的一部分。

First, note that when you type: 首先,请注意当您键入:

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

what sed sees as the script argument is actually: sed看到的script参数实际上是:

s/.cgi?key/.cgikey/g

because you didn't use any quotes to preserve the backslashes. 因为您没有使用任何引号来保留反斜杠。 (To demonstrate, use printf "%s\\n" s/\\.cgi\\?key/\\.cgikey/g , thus avoiding any questions of whether echo is interpreting the backslashes.) One side effect of this is that a URL such as: (为演示printf "%s\\n" s/\\.cgi\\?key/\\.cgikey/g ,请使用printf "%s\\n" s/\\.cgi\\?key/\\.cgikey/g ,从而避免出现任何有关echo是否正在解释反斜杠的问题。)这样做的副作用是URL这样如:

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

will be mapped to: 将映射到:

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

Using the single quotes when setting SED_OPTIONS ensures that the backslashes are preserved where required, and not putting a backslash before the ? 设置SED_OPTIONS时使用单引号可确保在需要的地方保留反斜杠,并且不要在?之前放置反斜杠? works. 作品。 I have both GNU sed and BSD sed on my Mac; 我的Mac上同时有GNU sed和BSD sed I've aliased them as gnu-sed and bsd-sed for clarity. 为了清楚起见,我将它们别名为gnu-sedbsd-sed Note that BSD sed requires a suffix for -i and won't accept standard input with -i . 请注意,BSD sed需要-i的后缀,并且不接受-i标准输入。 So, I've dropped the -i from the commands. 因此,我从命令中删除了-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
$

Note the difference in behaviour between the two versions of sed when there's a backslash before the question mark (second part of the example). 请注意,当问号前有反斜杠时,两个版本的sed行为有所不同(示例的第二部分)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM