比方说,你有一个Bash别名:
alias rxvt='urxvt'
这很好。
然而:
alias rxvt='urxvt -fg '#111111' -bg '#111111''
不管用,也不管用:
alias rxvt='urxvt -fg \'#111111\' -bg \'#111111\''
那么,一旦有转义引号,如何在字符串中匹配开始和结束引号呢?
alias rxvt='urxvt -fg'\''#111111'\'' -bg '\''#111111'\''
看起来很笨拙,但如果允许像这样连接它们,它将表示相同的字符串。
我总是将每个嵌入的单引号替换为序列:'\ "(即:quote反斜杠quote quote),它关闭字符串,附加一个转义的单引号并重新打开字符串。
我经常在Perl脚本中创建一个“quotify”函数来为我做这件事。步骤如下:
s/'/'\\''/g # Handle each embedded quote
$_ = qq['$_']; # Surround result with single quotes.
这基本上解决了所有情况。
当您在shell脚本中引入eval时,生活会变得更加有趣。你必须重新对所有东西进行报价!
例如,创建一个名为quotify的Perl脚本,包含上面的语句:
#!/usr/bin/perl -pl
s/'/'\\''/g;
$_ = qq['$_'];
然后使用它生成一个正确引用的字符串:
$ quotify
urxvt -fg '#111111' -bg '#111111'
结果:
'urxvt -fg '\''#111111'\'' -bg '\''#111111'\'''
然后可以复制/粘贴到alias命令:
alias rxvt='urxvt -fg '\''#111111'\'' -bg '\''#111111'\'''
(如果需要在eval中插入该命令,请再次运行quotify:
$ quotify
alias rxvt='urxvt -fg '\''#111111'\'' -bg '\''#111111'\'''
结果:
'alias rxvt='\''urxvt -fg '\''\'\'''\''#111111'\''\'\'''\'' -bg '\''\'\'''\''#111111'\''\'\'''\'''\'''
可以复制/粘贴到eval中:
eval 'alias rxvt='\''urxvt -fg '\''\'\'''\''#111111'\''\'\'''\'' -bg '\''\'\'''\''#111111'\''\'\'''\'''\'''
显然,简单地用双引号括起来会更容易,但这其中的挑战在哪里呢?下面是只用单引号的答案。我用的是变量而不是别名这样更容易打印出来证明,但这和使用别名是一样的。
$ rxvt='urxvt -fg '\''#111111'\'' -bg '\''#111111'\'
$ echo $rxvt
urxvt -fg '#111111' -bg '#111111'
解释
关键在于,您可以关闭单引号,并根据需要多次重新打开它。例如,foo='a " b'与foo='ab'相同。所以你可以关闭单引号,放入一个文字单引号\',然后重新打开下一个单引号。
分解图
这个图通过使用括号来显示单引号的开始和结束位置,使之更加清晰。引号不像圆括号那样“嵌套”。你也可以注意颜色的高亮,这是正确的应用。引号中的字符串是栗色的,而\'是黑色的。
'urxvt -fg '\''#111111'\'' -bg '\''#111111'\' # original
[^^^^^^^^^^] ^[^^^^^^^] ^[^^^^^] ^[^^^^^^^] ^ # show open/close quotes
urxvt -fg ' #111111 ' -bg ' #111111 ' # literal characters remaining
(这基本上和Adrian的答案是一样的,但我觉得这解释得更好。而且他的回答在结尾有两个多余的单引号。)
我总是将每个嵌入的单引号替换为序列:'\ "(即:quote反斜杠quote quote),它关闭字符串,附加一个转义的单引号并重新打开字符串。
我经常在Perl脚本中创建一个“quotify”函数来为我做这件事。步骤如下:
s/'/'\\''/g # Handle each embedded quote
$_ = qq['$_']; # Surround result with single quotes.
这基本上解决了所有情况。
当您在shell脚本中引入eval时,生活会变得更加有趣。你必须重新对所有东西进行报价!
例如,创建一个名为quotify的Perl脚本,包含上面的语句:
#!/usr/bin/perl -pl
s/'/'\\''/g;
$_ = qq['$_'];
然后使用它生成一个正确引用的字符串:
$ quotify
urxvt -fg '#111111' -bg '#111111'
结果:
'urxvt -fg '\''#111111'\'' -bg '\''#111111'\'''
然后可以复制/粘贴到alias命令:
alias rxvt='urxvt -fg '\''#111111'\'' -bg '\''#111111'\'''
(如果需要在eval中插入该命令,请再次运行quotify:
$ quotify
alias rxvt='urxvt -fg '\''#111111'\'' -bg '\''#111111'\'''
结果:
'alias rxvt='\''urxvt -fg '\''\'\'''\''#111111'\''\'\'''\'' -bg '\''\'\'''\''#111111'\''\'\'''\'''\'''
可以复制/粘贴到eval中:
eval 'alias rxvt='\''urxvt -fg '\''\'\'''\''#111111'\''\'\'''\'' -bg '\''\'\'''\''#111111'\''\'\'''\'''\'''
除了@JasonWoof的完美答案,我想展示我是如何解决相关问题的
在我的例子中,用“\”编码单引号并不总是足够的,例如,如果字符串必须用单引号引用,但是引号的总数导致奇数
#!/bin/bash
# no closing quote
string='alecxs\'solution'
# this works for string
string="alecxs'solution"
string=alecxs\'solution
string='alecxs'\''solution'
让我们假设字符串是一个文件名,我们需要在一个列表中保存引用的文件名(如stat -c%N ./* > list)
echo "'$string'" > "$string"
cat "$string"
但是处理这个列表将失败(取决于字符串总共包含多少引号)
while read file
do
ls -l "$file"
eval ls -l "$file"
done < "$string"
解决方法:用字符串操作编码引号
string="${string//$'\047'/\'\$\'\\\\047\'\'}"
# result
echo "$string"
现在它起作用了,因为报价总是平衡的
echo "'$string'" > list
while read file
do
ls -l "$file"
eval ls -l "$file"
done < list
希望在遇到类似问题时能有所帮助
另一种解决太多嵌套引用层问题的方法:
您试图将太多内容塞进太小的空间,因此使用bash函数。
问题是您试图有太多的嵌套级别,而基本的别名技术还不够强大。使用这样的bash函数,使单引号、双引号、反引号和传入的参数都像我们预期的那样正常处理:
lets_do_some_stuff() {
tmp=$1 #keep a passed in parameter.
run_your_program $@ #use all your passed parameters.
echo -e '\n-------------' #use your single quotes.
echo `date` #use your back ticks.
echo -e "\n-------------" #use your double quotes.
}
alias foobarbaz=lets_do_some_stuff
然后,您可以使用$1和$2变量以及单引号、双引号和反引号,而不用担心别名函数破坏它们的完整性。
这个程序输出:
el@defiant ~/code $ foobarbaz alien Dyson ring detected @grid 10385
alien Dyson ring detected @grid 10385
-------------
Mon Oct 26 20:30:14 EDT 2015
-------------