如何在单引号字符串中转义单引号?

时间:2009-08-08 22:50:11

标签: bash syntax quoting

比方说,你有一个bash alias喜欢:

alias rxvt='urxvt'

工作正常。

然而:

alias rxvt='urxvt -fg '#111111' -bg '#111111''

不起作用,也不会:

alias rxvt='urxvt -fg \'#111111\' -bg \'#111111\''

那么,一旦转义了引号,你最终如何在字符串中匹配开头和结尾的引号?

alias rxvt='urxvt -fg'\''#111111'\'' -bg '\''#111111'\''

看起来很笨拙但是如果允许你像这样连接它们就会代表相同的字符串。

24 个答案:

答案 0 :(得分:1237)

如果你真的想在最外层使用单引号,请记住你可以粘合两种报价。例如:

 alias rxvt='urxvt -fg '"'"'#111111'"'"' -bg '"'"'#111111'"'"
 #                     ^^^^^       ^^^^^     ^^^^^       ^^^^
 #                     12345       12345     12345       1234

解释'"'"'如何被解释为'

  1. '结束使用单引号的第一个引号。
  2. "使用双引号开始第二次引用。
  3. '引用字符。
  4. "使用双引号结束第二次引用。
  5. '使用单引号开始第三次引用。
  6. 如果你没有在(1)和(2)之间,或者在(4)和(5)之间放置任何空格,shell会将该字符串解释为一个长字。

答案 1 :(得分:228)

我总是用序列替换每个嵌入的单引号:'\''(即:引用反斜杠引用引号),它关闭字符串,附加转义的单引号并重新打开字符串。


我经常在Perl脚本中添加一个“quotify”函数来为我做这个。步骤将是:

s/'/'\\''/g    # Handle each embedded quote
$_ = qq['$_']; # Surround result with single quotes.

这几乎可以解决所有情况。

eval引入shell脚本时,生活会变得更有趣。你基本上必须重新引用一切!

例如,创建一个名为quotify的Perl脚本,其中包含上述语句:

#!/usr/bin/perl -pl
s/'/'\\''/g;
$_ = qq['$_'];

然后用它来生成一个正确引用的字符串:

$ quotify
urxvt -fg '#111111' -bg '#111111'

结果:

'urxvt -fg '\''#111111'\'' -bg '\''#111111'\'''

然后可以将其复制/粘贴到别名命令中:

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 'alias rxvt='\''urxvt -fg '\''\'\'''\''#111111'\''\'\'''\'' -bg '\''\'\'''\''#111111'\''\'\'''\'''\'''

答案 2 :(得分:151)

由于Bash 2.04语法$'string'(而不只是'string';警告:不要与$('string')混淆)是另一种引用机制,允许ANSI C-like escape sequences和扩展到单引号版本。

简单示例:

  $> echo $'aa\'bb'
  aa'bb

  $> alias myvar=$'aa\'bb'
  $> alias myvar
  alias myvar='aa'\''bb'

在你的情况下:

$> alias rxvt=$'urxvt -fg \'#111111\' -bg \'#111111\''
$> alias rxvt
alias rxvt='urxvt -fg '\''#111111'\'' -bg '\''#111111'\'''

常见的转义序列按预期工作:

\'     single quote
\"     double quote
\\     backslash
\n     new line
\t     horizontal tab
\r     carriage return

以下是man bash(版本4.4)的复制+粘贴相关文档:

$'string'形式的单词是专门处理的。单词扩展为字符串,替换为ANSI C标准指定的反斜杠转义字符。反斜杠转义序列(如果存在)按如下方式解码:

    \a     alert (bell)
    \b     backspace
    \e
    \E     an escape character
    \f     form feed
    \n     new line
    \r     carriage return
    \t     horizontal tab
    \v     vertical tab
    \\     backslash
    \'     single quote
    \"     double quote
    \?     question mark
    \nnn   the eight-bit character whose value is the octal 
           value nnn (one to three digits)
    \xHH   the eight-bit character whose value is the hexadecimal
           value HH (one or two hex digits)
    \uHHHH the Unicode (ISO/IEC 10646) character whose value is 
           the hexadecimal value HHHH (one to four hex digits)
    \UHHHHHHHH the Unicode (ISO/IEC 10646) character whose value 
               is the hexadecimal value HHHHHHHH (one to eight 
               hex digits)
    \cx    a control-x character

展开的结果是单引号,好像美元符号不存在一样。


有关详细信息,请参阅bash-hackers.org wiki上的Quotes and escaping: ANSI C like strings。另请注意,"Bash Changes"文件(overview here)提及了很多与$'string'引用机制相关的更改和错误修复。

根据unix.stackexchange.com How to use a special character as a normal one?,它应该在bash,zsh,mksh,ksh93和FreeBSD以及busybox中工作(有一些变化)。

答案 3 :(得分:48)

我没有看到他博客上的条目(链接请参阅?)但是根据gnu reference manual

  

用单引号括起字符   (''')保留字面值   引号中的每个字符。一个   单引号之间可能不会发生   单引号,即使前面有一个   反斜杠。

所以bash不会理解:

alias x='y \'z '

但是,如果用双引号括起来,可以这样做:

alias x="echo \'y "
> x
> 'y

答案 4 :(得分:26)

我可以确认在单引号字符串中使用'\''单引号确实可以在Bash中使用,并且可以用与线程早期的“粘合”参数相同的方式进行解释。假设我们有一个带引号的字符串:'A '\''B'\'' C'(这里的所有引号都是单引号)。如果将其传递给echo,则会打印以下内容:A 'B' C。 在每个'\''中,第一个引号关闭当前单引号字符串,以下\'将单个引号粘合到前一个字符串(\'是一种指定单引号而不启动的方法引用字符串),最后一个引号打开另一个单引号字符串。

答案 5 :(得分:15)

在shell中转义引号的简单示例:

$ echo 'abc'\''abc'
abc'abc
$ echo "abc"\""abc"
abc"abc

通过完成已经打开的一个('),放置一个(\'),然后打开另一个(')来完成。此语法适用于所有命令。它与第一个答案非常相似。

答案 6 :(得分:13)

两个版本都可以正常工作,可以使用转义的单引号字符(\')连接,也可以通过将单引号字符括在双引号(“'”)中来连接。

该问题的作者没有注意到在他最后一次逃避尝试结束时有一个额外的单引号('):

alias rxvt='urxvt -fg'\''#111111'\'' -bg '\''#111111'\''
           │         │┊┊|       │┊┊│     │┊┊│       │┊┊│
           └─STRING──┘┊┊└─STRIN─┘┊┊└─STR─┘┊┊└─STRIN─┘┊┊│
                      ┊┊         ┊┊       ┊┊         ┊┊│
                      ┊┊         ┊┊       ┊┊         ┊┊│
                      └┴─────────┴┴───┰───┴┴─────────┴┘│
                          All escaped single quotes    │
                                                       │
                                                       ?

正如您在上一段精彩的ASCII / Unicode艺术作品中所看到的,最后一个转义的单引号(\')后跟一个不必要的单引号(')。使用类似于Notepad ++中的语法高亮显示器可以证明非常有用。

对于另一个例子,情况也是如此:

alias rc='sed '"'"':a;N;$!ba;s/\n/, /g'"'"
alias rc='sed '\'':a;N;$!ba;s/\n/, /g'\'

这两个漂亮的别名实例以一种非常复杂和混淆的方式显示文件如何排列。也就是说,从包含大量行的文件中,只能在前一行的内容之间使用逗号和空格。为了理解之前的评论,以下是一个例子:

$ cat Little_Commas.TXT
201737194
201802699
201835214

$ rc Little_Commas.TXT
201737194, 201802699, 201835214

答案 7 :(得分:11)

我并没有专门解决引用问题,因为有时,考虑替代方法是合理的。

rxvt() { urxvt -fg "#${1:-000000}" -bg "#${2:-FFFFFF}"; }

然后您可以将其称为:

rxvt 123456 654321

这个想法是你现在可以在不关心引号的情况下对其进行别名:

alias rxvt='rxvt 123456 654321'

或者,如果您出于某种原因需要在所有通话中加入#

rxvt() { urxvt -fg "${1:-#000000}" -bg "${2:-#FFFFFF}"; }

然后您可以将其称为:

rxvt '#123456' '#654321'

然后,当然,别名是:

alias rxvt="rxvt '#123456' '#654321'"

(哎呀,我想我确实解决过这个问题:)

答案 8 :(得分:10)

我只是使用shell代码...例如适用的\x27\\x22。没有麻烦,真的。

答案 9 :(得分:9)

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

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

alias rxvt=$command

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

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

之内

答案 10 :(得分:6)

恕我直言,真正的答案是你不能在单引号字符串中转义单引号。

不可能。

如果我们假设我们正在使用bash。

来自bash手册...

Enclosing characters in single quotes preserves the literal value of each
character within the quotes.  A single quote may not occur
between single quotes, even when preceded by a backslash.

您需要使用其他字符串转义机制之一&#34;或\

alias要求它使用单引号没有任何魔力。

以下两项工作均采用bash。

alias rxvt="urxvt -fg '#111111' -bg '#111111'"
alias rxvt=urxvt\ -fg\ \'#111111\'\ -bg\ \'#111111\'

后者使用\来逃避空格字符。

#111111也没有什么神奇之处需要单引号。

以下选项与其他两个选项的结果相同,因为rxvt别名按预期工作。

alias rxvt='urxvt -fg "#111111" -bg "#111111"'
alias rxvt="urxvt -fg \"#111111\" -bg \"#111111\""

你也可以直接逃避麻烦的#

alias rxvt="urxvt -fg \#111111 -bg \#111111"

答案 11 :(得分:5)

这些答案中的大多数都是针对您要询问的具体案例。我和朋友开发的一种通用方法允许任意引用,以防您需要通过多层shell扩展引用bash命令,例如通过ssh,su -cbash -c等你需要一个核心原语,这里是原生bash:

quote_args() {
    local sq="'"
    local dq='"'
    local space=""
    local arg
    for arg; do
        echo -n "$space'${arg//$sq/$sq$dq$sq$dq$sq}'"
        space=" "
    done
}

这正是它所说的:它单独引用每个参数(当然是在bash扩展之后):

$ quote_args foo bar
'foo' 'bar'
$ quote_args arg1 'arg2 arg2a' arg3
'arg1' 'arg2 arg2a' 'arg3'
$ quote_args dq'"'
'dq"'
$ quote_args dq'"' sq"'"
'dq"' 'sq'"'"''
$ quote_args "*"
'*'
$ quote_args /b*
'/bin' '/boot'

对于一层扩展,它显而易见:

$ bash -c "$(quote_args echo a'"'b"'"c arg2)"
a"b'c arg2

(请注意,$(quote_args ...)周围的双引号是将结果转换为bash -c的单个参数所必需的。)并且它可以更普遍地用于通过多个扩展层正确引用:< / p>

$ bash -c "$(quote_args bash -c "$(quote_args echo a'"'b"'"c arg2)")"
a"b'c arg2

上面的例子:

  1. shell - 单独引用内部quote_args的每个参数,然后将结果输出合并为带有内部双引号的单个参数。
  2. shell-quotes bash-c以及步骤1中已经引用的结果,然后将结果合并到带有双引号的单个参数中。
  3. 将该混乱作为参数发送到外部bash -c
  4. 简而言之,这就是一个想法。你可以用这个来做一些非常复杂的事情,但你必须要小心评估的顺序以及引用哪些子串。例如,以下做错事(对于&#34;错误&#34;的某些定义):

    $ (cd /tmp; bash -c "$(quote_args cd /; pwd 1>&2)")
    /tmp
    $ (cd /tmp; bash -c "$(quote_args cd /; [ -e *sbin ] && echo success 1>&2 || echo failure 1>&2)")
    failure
    

    在第一个示例中,bash会立即将quote_args cd /; pwd 1>&2扩展为两个单独的命令quote_args cd /pwd 1>&2,因此/tmp时CWD仍为pwd执行命令。第二个例子说明了一个类似于globbing的问题。实际上,所有bash扩展都会出现同样的基本问题。这里的问题是命令替换不是一个函数调用:它实际上是在评估一个bash脚本并将其输出作为另一个bash脚本的一部分。

    如果你试图简单地转义shell运算符,你就会失败,因为传递给bash -c的结果字符串只是一系列单独引用的字符串,然后不会被解释为运算符,如果你回显传递给bash的字符串很容易看到:

    $ (cd /tmp; echo "$(quote_args cd /\; pwd 1\>\&2)")
    'cd' '/;' 'pwd' '1>&2'
    $ (cd /tmp; echo "$(quote_args cd /\; \[ -e \*sbin \] \&\& echo success 1\>\&2 \|\| echo failure 1\>\&2)")
    'cd' '/;' '[' '-e' '*sbin' ']' '&&' 'echo' 'success' '1>&2' '||' 'echo' 'failure' '1>&2'
    

    这里的问题是你过度引用。您需要的是操作符不加引号作为封闭bash -c的输入,这意味着它们需要在$(quote_args ...)命令替换之外。

    因此,在最一般意义上,您需要做的是在命令替换时单独引用命令的每个单词并不打算扩展,并且不对shell运算符应用任何额外的引用:< / p>

    $ (cd /tmp; echo "$(quote_args cd /); $(quote_args pwd) 1>&2")
    'cd' '/'; 'pwd' 1>&2
    $ (cd /tmp; bash -c "$(quote_args cd /); $(quote_args pwd) 1>&2")
    /
    $ (cd /tmp; echo "$(quote_args cd /); [ -e *$(quote_args sbin) ] && $(quote_args echo success) 1>&2 || $(quote_args echo failure) 1>&2")
    'cd' '/'; [ -e *'sbin' ] && 'echo' 'success' 1>&2 || 'echo' 'failure' 1>&2
    $ (cd /tmp; bash -c "$(quote_args cd /); [ -e *$(quote_args sbin) ] && $(quote_args echo success) 1>&2 || $(quote_args echo failure) 1>&2")
    success
    

    一旦你完成了这个,整个字符串就是公平的游戏,可以进一步引用任意级别的评估:

    $ bash -c "$(quote_args cd /tmp); $(quote_args bash -c "$(quote_args cd /); $(quote_args pwd) 1>&2")"
    /
    $ bash -c "$(quote_args bash -c "$(quote_args cd /tmp); $(quote_args bash -c "$(quote_args cd /); $(quote_args pwd) 1>&2")")"
    /
    $ bash -c "$(quote_args bash -c "$(quote_args bash -c "$(quote_args cd /tmp); $(quote_args bash -c "$(quote_args cd /); $(quote_args pwd) 1>&2")")")"
    /
    $ bash -c "$(quote_args cd /tmp); $(quote_args bash -c "$(quote_args cd /); [ -e *$(quote_args sbin) ] && $(quote_args echo success) 1>&2 || $(quote_args echo failure) 1>&2")"
    success
    $ bash -c "$(quote_args bash -c "$(quote_args cd /tmp); $(quote_args bash -c "$(quote_args cd /); [ -e *sbin ] && $(quote_args echo success) 1>&2 || $(quote_args echo failure) 1>&2")")"
    success
    $ bash -c "$(quote_args bash -c "$(quote_args bash -c "$(quote_args cd /tmp); $(quote_args bash -c "$(quote_args cd /); [ -e *$(quote_args sbin) ] && $(quote_args echo success) 1>&2 || $(quote_args echo failure) 1>&2")")")"
    success
    

    这些示例可能看起来过于紧张,因为successsbinpwd之类的字词不需要被引用,但是在撰写时需要记住的关键点一个采取任意输入的脚本是你想要引用你并不完全确定需要引用的所有内容,因为你永远不知道用户什么时候会引入{{1} }。

    为了更好地理解幕后发生的事情,你可以使用两个小帮助函数:

    Robert'; rm -rf /

    将在执行命令之前枚举命令的每个参数:

    debug_args() {
        for (( I=1; $I <= $#; I++ )); do
            echo -n "$I:<${!I}> " 1>&2
        done
        echo 1>&2
    }
    
    debug_args_and_run() {
        debug_args "$@"
        "$@"
    }
    

答案 12 :(得分:4)

在给定的示例中,只使用双引号而不是单引号作为外部转义机制:

alias rxvt="urxvt -fg '#111111' -bg '#111111'"

这种方法适用于您只想将固定字符串传递给命令的许多情况:只需检查shell如何通过echo解释双引号字符串,并在必要时使用反斜杠转义字符

在该示例中,您会看到双引号足以保护字符串:

$ echo "urxvt -fg '#111111' -bg '#111111'"
urxvt -fg '#111111' -bg '#111111'

答案 13 :(得分:3)

以下是上面提到的The One True Answer的详细说明:

有时我会在ssh上使用rsync下载并且必须使用&#39;来转义文件名。在它TWICE! (天啊!)一次用于bash,一次用于ssh。交替引用分隔符的原理相同。

例如,让我们说我们想得到:Louis Theroux的洛杉矶故事...

  1. 首先,您将路易斯Theroux用单引号括起来用于bash和双引号用于ssh: &#39;&#34;路易斯Theroux&#34;&#39;
  2. 然后你用单引号来逃避双引号&#39;&#34;&#39;
  3. 使用双引号来逃避撇号&#34;&#39;&#34;&#34;
  4. 然后重复#2,使用单引号来逃避双引号&#39;&#34;&#39;
  5. 然后将LA故事用单引号括起来用于bash和双引号ssh:&#39;&#34; LA Stories&#34;&#39;
  6. 看哪!你结束了这个:

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

    对于一个小小的工作来说,这是一项非常艰巨的工作。 - 但是你去了

答案 14 :(得分:2)

如何使用十六进制八进制字符转义单引号(')和双引号("

如果使用诸如 echo 之类的东西,我遇到过一些非常复杂、非常奇怪且难以逃脱(认为:非常嵌套)的情况,其中我唯一可以使用的就是使用八进制或十六进制代码!

以下是一些示例:

1.单引号示例,其中 ' 使用 hex \x27octal \047(其对应的 ASCII code)进行转义:

# 1. hex
$ echo -e "Let\x27s get coding!"
Let's get coding!

# 2. octal
$ echo -e "Let\047s get coding!"
Let's get coding!

2.双引号示例,其中 " 使用 hex \x22octal \042(其对应的 ASCII code)进行转义。

注意:bash 太疯狂了! Sometimes even the ! char has special meaning,并且必须从双引号内删除然后转义 "like this"\! 或完全放在单引号 'like this!' 内,而不是双引号内。< /p>

# 1. hex; escape `!` by removing it from within the double quotes 
# and escaping it with `\!`
$ echo -e "She said, \x22Let\x27s get coding"\!"\x22"
She said, "Let's get coding!"

# OR put it all within single quotes:
$ echo -e 'She said, \x22Let\x27s get coding!\x22'
She said, "Let's get coding!"


# 2. octal; escape `!` by removing it from within the double quotes 
$ echo -e "She said, \042Let\047s get coding"\!"\042"
She said, "Let's get coding!"

# OR put it all within single quotes:
$ echo -e 'She said, \042Let\047s get coding!\042'
She said, "Let's get coding!"


# 3. mixed hex and octal, just for fun
# escape `!` by removing it from within the double quotes when it is followed by
# another escape sequence
$ echo -e "She said, \x22Let\047s get coding! It\x27s waaay past time to begin"\!"\042"
She said, "Let's get coding! It's waaay past time to begin!"

# OR put it all within single quotes:
$ echo -e 'She said, \x22Let\047s get coding! It\x27s waaay past time to begin!\042'
She said, "Let's get coding! It's waaay past time to begin!"

请注意,如果您在需要时没有正确转义 !,正如我在上面展示的两种方法,您会得到一些奇怪的错误,如下所示:

$ echo -e "She said, \x22Let\047s get coding! It\x27s waaay past time to begin!\042"
bash: !\042: event not found

或:

$ echo -e "She said, \x22Let\x27s get coding!\x22"
bash: !\x22: event not found

参考:

  1. https://en.wikipedia.org/wiki/ASCII#Printable_characters
  2. https://serverfault.com/questions/208265/what-is-bash-event-not-found/208266#208266
  3. 另见我的另一个答案:How do I write non-ASCII characters using echo?

答案 15 :(得分:2)

除了@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

希望这在遇到类似问题时会有所帮助

答案 16 :(得分:2)

这是我的两分钱-如果要是sh便携式,而不只是bash专用(这种解决方案效率不高,因为它启动了外部程序-sed):

  • 将此quote.sh(或仅quote)放在您PATH上的某个位置:
# 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运行脚本时,脚本不会挂起。)

答案 17 :(得分:2)

解决嵌套引用层次太多问题的另一种方法:

你试图将太多的空间塞入太小的空间,所以使用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
-------------

答案 18 :(得分:1)

显然,简单地用双引号括起来会更容易,但那里的挑战在哪里呢?这是仅使用单引号的答案。我使用的是变量而不是alias,因此打印时更容易打样,但与使用alias相同。

$ 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的答案基本相同,但我觉得这更好地解释了。而且他的答案最后有2个多余的单引号。)

答案 19 :(得分:1)

如果已安装GNU Parallel,则可以使用其内部引号:

$ parallel --shellquote
L's 12" record
<Ctrl-D>
'L'"'"'s 12" record'
$ echo 'L'"'"'s 12" record'
L's 12" record

从版本20190222开始,您甚至可以多次--shellquote

$ parallel --shellquote --shellquote --shellquote
L's 12" record
<Ctrl-D>
'"'"'"'"'"'"'L'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'s 12" record'"'"'"'"'"'"'
$ eval eval echo '"'"'"'"'"'"'L'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'s 12" record'"'"'"'"'"'"'
L's 12" record

它将在所有受支持的shell(不仅是bash)中引用字符串。

答案 20 :(得分:1)

此功能:

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"'

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

答案 21 :(得分:0)

如果您在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!'

答案 22 :(得分:0)

shell_escape () {
    echo -nE "'${1//\'/\'\\\'\'}'"
}

实施说明:

  • 双引号,因此我们可以轻松输出包装单引号并使用${...}语法

  • bash的搜索和替换看起来像:${varname//search/replacement}

  • 我们将'替换为'\''

  • '\''像这样对单个'进行编码:

    1. '结束单引号

    2. \'编码'(需要反斜杠,因为我们不在引号内)

    3. '重新开始单引号

    4. bash自动连接字符串,字符串之间没有空格

  • 在每个\\之前都有一个',因为这是${...//.../...}的转义规则。

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

P.S。始终编​​码为单引号字符串,因为它们比双引号字符串更简单。

答案 23 :(得分:0)

这是另一种解决方案。这个函数只需要一个参数,并使用单引号字符恰当地引用它,就像上面的投票答案所解释的那样:

single_quote() {
  local quoted="'"
  local i=0
  while [ $i -lt ${#1} ]; do
    local ch="${1:i:1}"
    if [[ "$ch" != "'" ]]; then
      quoted="$quoted$ch"
    else
      local single_quotes="'"
      local j=1
      while [ $j -lt ${#1} ] && [[ "${1:i+j:1}" == "'" ]]; do
        single_quotes="$single_quotes'"
        ((j++))
      done
      quoted="$quoted'\"$single_quotes\"'"
      ((i+=j-1))
    fi
    ((i++))
  done
  echo "$quoted'"
}

所以,你可以这样使用它:

single_quote "1 2 '3'"
'1 2 '"'"'3'"'"''

x="this text is quoted: 'hello'"
eval "echo $(single_quote "$x")"
this text is quoted: 'hello'