比方说,你有一个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'\''

看起来很笨拙,但如果允许像这样连接它们,它将表示相同的字符串。


当前回答

下面是我的观点——如果一个人想要sh可移植,而不仅仅是特定于bash(解决方案不是很有效,但是,因为它启动了一个外部程序——sed):

把它放在你的PATH中的quote.sh(或者只是引用)中:

# this works with standard input (stdin)
quote() {
  echo -n "'" ;
  sed 's/\(['"'"']['"'"']*\)/'"'"'"\1"'"'"'/g' ;
  echo -n "'"
}

case "$1" in
 -) quote ;;
 *) echo "usage: cat ... | quote - # single-quotes input for Bourne shell" 2>&1 ;;
esac

一个例子:

$ echo -n "G'day, mate!" | ./quote.sh -
'G'"'"'day, mate!'

当然,它会转换回:

$ echo 'G'"'"'day, mate!'
G'day, mate!

解释:基本上我们必须用引号包围输入',然后也用这个微型怪物替换任何单引号:' ' ' ' '(以配对结束开头引号',通过双引号包装它来转义找到的单引号- "'",然后最后发出一个新的开头单引号',或者在伪符号中:' + "' ' + ' == ' ' ' ' ')

一种标准的方法是使用sed和下面的替换命令:

s/\(['][']*\)/'"\1"'/g 

但是,有一个小问题是,为了在shell中使用它,需要在sed表达式本身中转义所有这些单引号字符——这会导致类似的结果

sed 's/\(['"'"']['"'"']*\)/'"'"'"\1"'"'"'/g' 

(构建此结果的一个好方法是将原始表达式s/\(['][']*\)/'"\1"'/g提供给Kyle Rose'或George V. Reilly的脚本)。

最后,期望输入来自stdin是有意义的——因为通过命令行参数传递它可能已经太麻烦了。

(哦,也许我们想要添加一个小的帮助消息,这样当有人以。/quote.sh运行脚本时,脚本不会挂起——help想知道它是做什么的。)

其他回答

以下是上面提到的唯一正确答案的详细说明:

有时我会在ssh上使用rsync下载,并且必须转义一个带有'两次!(天啊!)一次用于bash,一次用于ssh。这里也使用了相同的交替引用分隔符原则。

例如,假设我们想要:Louis Theroux的LA Stories……

首先,将Louis Theroux用单引号括起来表示bash,用双引号括起来表示ssh: “路易泰鲁”的 然后使用单引号转义双引号" " 使用双引号来转义撇号"'" 然后重复#2,使用单引号转义双引号“” 然后将LA Stories包含在bash的单引号中,ssh的双引号中:' ' LA Stories ' '

看哪!你会得到这样的结果:

rsync -ave ssh '"Louis Theroux"''"'"'"'"''"s LA Stories"'

对一个人来说,这是一件可怕的事情——但你明白了吗

由于不能在单引号字符串中放入单引号,因此最简单且最易读的选项是使用HEREDOC字符串

command=$(cat <<'COMMAND'
urxvt -fg '#111111' -bg '#111111'
COMMAND
)

alias rxvt=$command

在上面的代码中,HEREDOC被发送给cat命令,该命令的输出通过命令替换符号$(..)分配给一个变量。

需要在HEREDOC周围加上单引号,因为它在$()中

这个函数:

quote () 
{ 
    local quoted=${1//\'/\'\\\'\'};
    printf "'%s'" "$quoted"
}

允许引用“内部”。像这样使用:

$ quote "urxvt -fg '#111111' -bg '#111111'"
'urxvt -fg '\''#111111'\'' -bg '\''#111111'\'''

如果要引用的行变得更复杂,比如双引号和单引号混合在一起,那么在变量中获得要引用的字符串可能会变得相当棘手。当出现这种情况时,在脚本中编写需要引用的确切行(类似于此)。

#!/bin/bash

quote ()
{
    local quoted=${1//\'/\'\\\'\'};
    printf "'%s'" "$quoted"
}

while read line; do
    quote "$line"
done <<-\_lines_to_quote_
urxvt -fg '#111111' -bg '#111111'
Louis Theroux's LA Stories
'single quote phrase' "double quote phrase"
_lines_to_quote_

将输出:

'urxvt -fg '\''#111111'\'' -bg '\''#111111'\'''
'Louis Theroux'\''s LA Stories'
''\''single quote phrase'\'' "double quote phrase"'

所有在单引号内正确引用的字符串。

如果你在Python 2或Python 3中生成shell字符串,下面的语句可能有助于引用参数:

#!/usr/bin/env python

from __future__ import print_function

try:  # py3
    from shlex import quote as shlex_quote
except ImportError:  # py2
    from pipes import quote as shlex_quote

s = """foo ain't "bad" so there!"""

print(s)
print(" ".join([shlex_quote(t) for t in s.split()]))

这将输出:

foo ain't "bad" so there!
foo 'ain'"'"'t' '"bad"' so 'there!'
shell_escape () {
    printf '%s' "'${1//\'/\'\\\'\'}'"
}

实现说明:

双引号,这样我们可以很容易地输出换行单引号,并使用${…}语法 Bash的搜索和替换如下:${varname//search/replacement} 我们用“\”代替“\” '\ "编码单个',如下所示: 单引号结束 \'编码a '(需要反斜杠,因为我们不在引号内) 又开始了单引号 Bash自动连接字符串,中间没有空格 在每个\和`之前都有一个\,因为这是${…//…/…的转义规则。}。

string="That's "'#@$*&^`(@#'
echo "original: $string"
echo "encoded:  $(shell_escape "$string")"
echo "expanded: $(bash -c "echo $(shell_escape "$string")")"

注:始终编码为单引号字符串,因为它们比双引号字符串简单得多。