如何在单引号引起来的字符串中转义单引号


1015

假设您有一个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'\''

看起来很笨拙,尽管如果允许这样将它们串联起来,它将表示相同的字符串。


16
您是否意识到您不需要对别名使用单引号?双引号要​​容易得多。
teknopaul


3
可以使用嵌套的双引号"\"",因此,应尽可能使用@liori的答案。
阿兰

7
双引号的行为与* nix中的单引号(包括Bash和相关工具(如Perl))有很大的不同,因此,只要单引号出现问题,就用双引号代替不是一个好的解决方案。双引号指定$ ...将在执行前替换变量,而单引号指定$ ...将按字面意义处理。
Chuck Kollars '19

如果您在考虑,我使用了双引号,但仍然无法使用,请再次获取脚本。
Samy Bencherif

Answers:


1452

如果您确实想在最外层使用单引号,请记住可以同时粘贴两种引号。例:

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

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

  1. ' 结束使用单引号的第一引号。
  2. " 使用双引号开始第二个引号。
  3. ' 带引号的字符。
  4. " 使用双引号结束第二个引号。
  5. ' 用单引号开始第三个引号。

如果您没有在(1)和(2)之间或(4)和(5)之间放置任何空格,则Shell会将该字符串解释为一个长字。


5
alias splitpath='echo $PATH | awk -F : '"'"'{print "PATH is set to"} {for (i=1;i<=NF;i++) {print "["i"]",$i}}'"'"当别名字符串中同时包含单引号和双引号时,它将起作用!
Uphill_ What'1

17
我的解释:bash隐式连接了用不同引号引起来的字符串表达式。
本杰明·阿特金

2
为我工作,双转义单引号的示例:alias serve_this_dir='ruby -rrack -e "include Rack;Handler::Thin.run Builder.new{run Directory.new'"'"''"'"'}"'
JAMESSTONEco 2013年

2
当然不是最易读的解决方案。它在真正不需要单引号的地方过度使用。

26
我认为,'\''在大多数情况下,它比可读性强得多'"'"'。实际上,前者在单引号字符串中几乎总是明显不同,因此就像将其在语义上映射到“这是一个转义的引号”一样,就像\"在双引号字符串中所做的那样。后者混为一谈,并在许多情况下需要仔细检查以正确区分。
mtraceur's

263

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


我经常在Perl脚本中鞭打一个“报价”功能来为我做这件事。步骤将是:

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命令中:

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

(如果您需要将命令插入到评估中,请再次运行quotify:

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

结果:

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

可以将其复制/粘贴到评估中:

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

1
但这不是perl。并且正如史蒂夫·鲍勃(Steve B)上文指出的那样,在他引用“ gnu参考手册”的情况下,您不能在同一引用类型中的bash中转义引用。而且实际上,不需要在备用引号中转义它们,例如,“'”是有效的单引号字符串,而“”'是有效的双引号字符串,而无需进行任何转义
。– nicerobot

8
@nicerobot:我添加了一个示例,该示例显示:1)我不会尝试对相同类型的引号内的引号进行转义,2)也不对其他引号进行转义,并且3)Perl用于自动化生成有效报价的过程bash字符串包含嵌入引号
Adrian Pronk

18
第一段本身就是我一直在寻找的答案。
戴夫·凯西

9
这也是bash所做的事情,键入set -xecho "here's a string"然后您将看到bash执行echo 'here'\''s a string'。(set +x返回正常行为)
arekolek '16

195

由于Bash 2.04语法$'string'(而不是'string';警告:请不要与混淆$('string'))是另一种引用机制,它允许类似ANSI C的转义序列并扩展为单引号版本。

简单的例子:

  $> 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上的引号和转义:类似于ANSI C的字符串。另请注意,“ Bash更改”文件(此处概述)提到了很多与$'string'报价机制相关的更改和错误修复。

根据unix.stackexchange.com,如何使用特殊字符作为普通字符?它应该在bash,zsh,mksh,ksh93和FreeBSD和busybox sh中工作(有一些变体)。


可以使用,但这里的单引号字符串不是真正的单引号,该字符串的内容可能由外壳解释:echo $'foo\'b!ar'=> !ar': event not found
regilero 2014年

2
在我的机器上 > echo $BASH_VERSION 4.2.47(1)-release > echo $'foo\'b!ar' foo'b!ar
mj41

1
是的,这就是“可能”的原因,我在Red hat 6.4(肯定是较旧的bash版本)上安装了它。
regilero 2014年

Bash ChangeLog包含许多与相关的错误修复,$'因此,最简单的方法可能是在较旧的系统上自行尝试。
mj41 2014年

请注意:e. Bash no longer inhibits C-style escape processing ($'...') while performing pattern substitution word expansions.摘自tiswww.case.edu/php/chet/bash/CHANGES。在4.3.42中仍然有效,但在4.3.48中无效。
stiller_leser

49

我没有在他的博客上看到该条目(请链接?),但是根据gnu参考手册

将字符括在单引号(''')中可保留引号内每个字符的字面值。即使在单引号之前加反斜杠,也不能在单引号之间引起单引号。

所以bash无法理解:

alias x='y \'z '

但是,如果用双引号引起来,则可以执行以下操作:

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


正在对用双引号引起来的内容进行评估,因此按照liori的建议,仅将单引号括在双引号中似乎是正确的解决方案。
Piotr Dobrogost 2012年

3
这是对该问题的实际答案。尽管接受的答案可能会提供解决方案,但从技术上讲,它是在回答未提出的问题。
马修·G

3
马修,问题是关于在单引号内转义单引号。该答案要求用户更改其行为,并且如果您有使用双引号的障碍(如问题标题所示),此答案将无济于事。尽管它很有用(尽管很明显),因此值得赞扬,但是公认的答案解决了Op询问的确切问题。
Fernando Cordeiro

无需在双引号字符串中用单引号引起来。
马修·斯科尔菲尔德

32

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


2
这具有误导性,该语法“ \”不会放在单个引号字符串的“内部”。在此语句'A'\'B'\'C'中,您要连接5 \转义和单引号字符串
teknopaul

1
@teknopaul赋值alias something='A '\''B'\'' C'确实导致something是单个字符串,因此即使从技术上讲赋值的右侧不是单个字符串,我也不认为这很重要。
Teemu Leisti

尽管在您的示例中可以使用此方法,但从技术上讲,它并没有提供有关如何单个带引号的字符串中插入单个引号的解决方案。您已经解释了它,但是是的'A ' + ' + 'B' + ' + ' C'。换句话说,一种在单引号字符串中插入单引号字符的解决方案应该允许我自己创建并打印这样的字符串。但是,这种解决方案在这种情况下将不起作用。 STR='\''; echo $STR。按照设计,BASH并没有真正允许这样做。
krb686 '16

@mikhail_b是的,'\''适用于bash。您能否指出gnu.org/software/bash/manual/bashref.html的哪些部分指定了这种行为?
姚敬国

20

两种版本都可以使用,通过使用转义的单引号字符(\')进行串联,或者通过将单引号字符括在双引号(“'”)中进行串联。

问题的作者没有注意到在他最后一次转义尝试的末尾有一个额外的单引号('):

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

3
为ASCII表插图敬上:)
php-dev

16

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

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

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


15

我并不是专门解决报价问题,因为有时候考虑替代方法是合理的。

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

(糟糕,我想我确实解决了这个问题:)


1
我试图将某些内容放在单引号中,将双引号引起来,将双引号反过来又变成单引号。kes。感谢您回答“尝试其他方法”。这有所作为。
克林顿·布莱克莫尔

1
我迟到了5年,但是您是否没有在上一个别名中缺少单引号?
朱利安

1
@Julien我没看到问题;-)
nicerobot

11

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

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

alias rxvt=$command

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

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


我希望我已经向下滚动了很远-我重新发明了这种方法并来到这里发布!与所有其他转义方法相比,此方法要干净得多且可读性强。并非如此,它不适用于某些非bash shell,例如dashUbuntu upstart脚本和其他地方的默认shell。
Korny

谢谢!我要找的东西,就是通过heredoc定义命令并将自动转义的命令传递给ssh的方式。顺便说一句,猫<<不带引号的命令允许在命令内部插入变量,并且对于这种方法也适用。
Igor Tverdovskiy

10

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


您可以举一个运行中的例子吗?对我来说,它只是打印文字x27(在Centos 6.6上)
Will Sheppard

6

这些答案大多数都针对您要询问的特定案例。有一种普遍的做法,我和一个朋友已经开发出允许的情况下,随意引用您需要引用的bash命令通过shell扩展,如多层,通过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
}

这正是它所说的:它分别对每个参数用shell引号(当然,在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。)它可以更广泛地用于在多层扩展中正确引用:

$ 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

简而言之,这就是想法。您可以用它来做一些非常复杂的事情,但是您必须注意求值顺序和所引用的子字符串。例如,以下操作做错了事情(对于“错误”的某些定义):

$ (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,因此执行/tmppwd命令时CWD仍处于静止状态。第二个示例说明了类似的问题。确实,所有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引用命令替换时不打算扩展的命令的每个单词,并且不对shell运算符应用任何额外的引用:

$ (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

等等

考虑到诸如successsbin和的字样,这些示例似乎显得pwd有些劳损,但是在编写使用任意输入的脚本时要记住的关键点是,您想引用所有不确定的内容,牛逼需要报价,因为你永远不知道什么时候,用户将在一个扔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 "$@"
    "$@"
}

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

$ debug_args_and_run echo a'"'b"'"c arg2
1:<echo> 2:<a"b'c> 3:<arg2> 
a"b'c arg2

$ bash -c "$(quote_args debug_args_and_run echo a'"'b"'"c arg2)"
1:<echo> 2:<a"b'c> 3:<arg2> 
a"b'c arg2

$ bash -c "$(quote_args debug_args_and_run bash -c "$(quote_args debug_args_and_run echo a'"'b"'"c arg2)")"
1:<bash> 2:<-c> 3:<'debug_args_and_run' 'echo' 'a"b'"'"'c' 'arg2'> 
1:<echo> 2:<a"b'c> 3:<arg2> 
a"b'c arg2

$ bash -c "$(quote_args debug_args_and_run bash -c "$(quote_args debug_args_and_run bash -c "$(quote_args debug_args_and_run echo a'"'b"'"c arg2)")")"
1:<bash> 2:<-c> 3:<'debug_args_and_run' 'bash' '-c' ''"'"'debug_args_and_run'"'"' '"'"'echo'"'"' '"'"'a"b'"'"'"'"'"'"'"'"'c'"'"' '"'"'arg2'"'"''> 
1:<bash> 2:<-c> 3:<'debug_args_and_run' 'echo' 'a"b'"'"'c' 'arg2'> 
1:<echo> 2:<a"b'c> 3:<arg2> 
a"b'c arg2

$ bash -c "$(quote_args debug_args_and_run bash -c "$(quote_args debug_args_and_run bash -c "$(quote_args debug_args_and_run bash -c "$(quote_args debug_args_and_run echo a'"'b"'"c arg2)")")")"
1:<bash> 2:<-c> 3:<'debug_args_and_run' 'bash' '-c' ''"'"'debug_args_and_run'"'"' '"'"'bash'"'"' '"'"'-c'"'"' '"'"''"'"'"'"'"'"'"'"'debug_args_and_run'"'"'"'"'"'"'"'"' '"'"'"'"'"'"'"'"'echo'"'"'"'"'"'"'"'"' '"'"'"'"'"'"'"'"'a"b'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'c'"'"'"'"'"'"'"'"' '"'"'"'"'"'"'"'"'arg2'"'"'"'"'"'"'"'"''"'"''> 
1:<bash> 2:<-c> 3:<'debug_args_and_run' 'bash' '-c' ''"'"'debug_args_and_run'"'"' '"'"'echo'"'"' '"'"'a"b'"'"'"'"'"'"'"'"'c'"'"' '"'"'arg2'"'"''> 
1:<bash> 2:<-c> 3:<'debug_args_and_run' 'echo' 'a"b'"'"'c' 'arg2'> 
1:<echo> 2:<a"b'c> 3:<arg2> 
a"b'c arg2

嗨,凯尔。当我需要将一组参数作为单个参数传递时,您的解决方案对我遇到的情况非常有用vagrant ssh -c {single-arg} guest。在{single-arg}因为流浪汉后,将在下次ARG作为客人姓名需要被视为一个单一的精氨酸。顺序无法更改。但我需要在中传递命令及其参数{single-arg}。因此,我使用您quote_args()在命令及其参数中加了引号,并对结果加上了双引号,它的工作原理就像一个魅力:vagrant ssh -c "'command' 'arg 1 with blanks' 'arg 2'" guest。谢谢!!!
安德烈亚斯·迈尔

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.

您需要使用其他字符串转义机制之一“或\

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"

“真正的答案是,您不能在单引号引起来的字符串中转义单引号。” 从技术上讲是正确的。但是您可以有一个以单引号开头,以单引号结尾并且在中间仅包含单引号的解决方案。stackoverflow.com/a/49063038
wisbucky

不是通过转义,而是通过串联。
teknopaul

4

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

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

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

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

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

4

显然,仅用双引号括起来会更容易,但是挑战在哪里呢?这是仅使用单引号的答案。我使用的是变量,而不是变量,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个多余的单引号。)


'\''我建议使用+1,'"'"'而不是通常难以阅读的方法。
mtraceur

3

以下是上述“一个真实答案”的详细说明:

有时我会在ssh上使用rsync进行下载,并且必须在其中两次转义带有'的文件名!(OMG!)一次用于bash,一次用于ssh。交替引用定界符的相同原理在这里起作用。

例如,假设我们要获取:Louis Theroux的LA Stories ...

  1. 首先,您将Louis Theroux括在bash的单引号和ssh的双引号中:'“ Louis Theroux”'
  2. 然后,您使用单引号将双引号'“'转义
  3. 使用双引号转义撇号“'”
  4. 然后重复#2,使用单引号转义双引号'“'
  5. 然后将LA Stories括在bash的单引号和ssh的双引号中:'“ LA Stories”'

瞧!您结束了:

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

一个小小的工作量很大-但是你去了


3
shell_escape () {
    printf '%s' "'${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")")"

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


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

2

如果安装了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)引用该字符串。


1

该功能:

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

允许引用'inside '。用作:

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

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


1

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

1

这是我的两分钱-如果是要sh随身携带,而不仅仅是bash特定于-(解决方案在启动外部程序时效率不高- sed):

  • 将其放在您的某处quote.sh(或只是quotePATH
#这适用于标准输入(stdin)
quote(){
  echo -n“'”;
  sed's / \(['“'”'] ['“'”'] * \)/'“'”'“ \\ 1”'“'”'/ g';
  回声-n“'”
}

案例“ $ 1”在
 -)报价;;
 *)echo“用法:cat ... | quote-#伯恩壳的单引号输入” 2>&1 ;;
埃萨克

一个例子:

$ echo -n“天啊,伙计!” | ./quote.sh-
'G'“'”'天,队友!'

当然,这会转换回:

$ echo'G'“'''天,队友!'
天啊,伙计!

说明:基本上,我们必须用双引号将输入括起来',然后还要用此微型怪兽替换其中的任何单引号:('"'"'用一对配对结束开头的引号,'通过用双引号将它包装起来来转义找到的单引号- "'"和后来终于发出新开单引号',或在伪符号:' + "'" + ' == '"'"'

一种标准的方法是sed与以下替换命令一起使用:

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

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

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

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

最后,期望输入来自stdin-从命令行参数传递输入可能已经太麻烦了。

(哦,也许我们想要添加一条小的帮助消息,以便当有人只是./quote.sh --help想知道脚本的功能而运行脚本时,脚本不会挂起。)


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'
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.