1239

比方说,你有一个alias像这样的 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'\''

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

4

25 回答 25

1721

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

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

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

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

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

于 2009-08-09T00:52:37.837 回答
321

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


我经常在我的 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 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'\''\'\'''\'''\'''
于 2009-08-22T05:30:14.560 回答
262

由于 Bash 2.04语法$'string'允许有限的转义集。

从 Bash 4.4 开始,$'string'也允许全套C 风格的转义$'string',使得行为在以前的版本中略有不同。(以前$('string')可以使用该表格。)

Bash 2.04 和更新版本中的简单示例:

  $> 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 Changes”文件(此处概述$'string')提到了很多与引用机制相关的更改和错误修复。

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

于 2013-05-17T08:56:20.527 回答
55

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

将字符括在单引号 (''') 中会保留引号内每个字符的文字值。单引号之间不能出现单引号,即使前面有反斜杠。

所以 bash 不会理解:

alias x='y \'z '

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

alias x="echo \'y "
> x
> 'y
于 2009-08-08T23:07:35.800 回答
41

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

于 2011-08-03T07:54:16.513 回答
23

两个版本都可以使用,或者通过使用转义的单引号字符 (\') 进行连接,或者通过将单引号字符括在双引号 ("'") 中进行连接。

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

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
于 2015-02-20T08:08:10.513 回答
16

我没有具体解决引用问题,因为,有时候,考虑一种替代方法是合理的。

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

(哎呀,我想我确实提到了引用:)

于 2009-08-22T04:59:09.567 回答
16

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

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

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

于 2015-02-28T20:59:23.517 回答
12

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

于 2014-12-16T16:15:06.520 回答
12

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

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

alias rxvt=$command

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

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

于 2016-02-12T09:53:27.640 回答
9

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

不可能。

如果我们假设我们正在使用 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"
于 2016-01-09T23:47:57.903 回答
9

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

如果使用类似的东西echo,我遇到了一些非常复杂、非常奇怪和难以逃避(想想:非常嵌套)的情况,我唯一能做的就是使用八进制或十六进制代码!

以下是一些基本示例,只是为了演示它是如何工作的:

1.单引号示例,其中'十六进制 \x27八进制 \047转义(其对应的ASCII码):

  1. 十六进制\x27

    echo -e "Let\x27s get coding!"
    # OR
    echo -e 'Let\x27s get coding!'
    

    结果:

    Let's get coding!
    
  2. 八进制\047

    echo -e "Let\047s get coding!"
    # OR
    echo -e 'Let\047s get coding!'
    

    结果:

    Let's get coding!
    

2. 双引号示例,其中用十六进制八进制(其对应的ASCII码")进行转义。 \x22 \042

注意:bash是坚果!有时甚至!char 也有特殊含义,必须从双引号中删除,然后转义"like this"\!或完全放在单引号内'like this!',而不是放在双引号内。

# 1. hex; also 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; also 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
# also 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

另一种选择:这允许在同一个 bash 字符串中混合扩展和非扩展

这是另一种转义技术的另一个演示。

首先,阅读@liori 的主要答案,看看下面的第二种形式是如何工作的。现在,阅读这两种转义字符的替代方法。以下两个示例的输出相同:

CMD="gs_set_title"

# 1. 1st technique: escape the $ symbol with a backslash (\) so it doesn't 
# run and expand the command following it
echo "$CMD '\$(basename \"\$(pwd)\")'"

# 2. 2nd technique (does the same thing in a different way): escape the 
# $ symbol using single quotes around it, and the single quote (') symbol
# using double quotes around it
echo "$CMD ""'"'$(basename "$(pwd)")'"'"

样本输出:

gs_set_title '$(basename "$(pwd)")'
gs_set_title '$(basename "$(pwd)")'

注意:对于我此处某处的文件gs_set_title中的bash 函数,请在此处查看我的其他答案~/.bash_aliases

参考:

  1. https://en.wikipedia.org/wiki/ASCII#Printable_characters
  2. https://serverfault.com/questions/208265/what-is-bash-event-not-found/208266#208266
  3. 另请参阅我的其他答案:如何使用 echo 编写非 ASCII 字符?.
于 2021-01-25T04:28:45.547 回答
7

这些答案中的大多数都针对您要询问的具体案例。我和朋友开发了一种通用方法,允许任意引用,以防您需要通过多层 shell 扩展引用 bash 命令,例如,通过 ssh、、、su -cbash -c。您需要一个核心原语,here在本机 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。)并且它可以更普遍地用于通过多层扩展正确引用:

$ 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>&2CWD 仍然存在。第二个示例说明了类似的通配问题。实际上,所有 bash 扩展都会出现相同的基本问题。这里的问题是命令替换不是函数调用:它实际上是在评估一个 bash 脚本并将其输出用作另一个 bash 脚本的一部分。/tmppwd

如果您尝试简单地转义 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

等等

success考虑到诸如,sbin和之类的词pwd不需要在 shell 中引用,这些示例可能看起来有些过头了,但是在编写带有任意输入的脚本时要记住的关键点是,您要引用所有不确定的内容不需要引用,因为您永远不知道用户何时会输入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
于 2015-12-29T16:57:04.067 回答
4

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

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

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

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

$ echo "urxvt -fg '#111111' -bg '#111111'"
urxvt -fg '#111111' -bg '#111111'
于 2013-11-25T14:36:57.827 回答
4

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

有时我会在 ssh 上使用 rsync 进行下载,并且必须使用 ' 转义文件名两次!(天哪!)一次用于 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"'

对于一个小家伙来说,这是一项非常艰巨的工作——但是你去吧

于 2014-04-07T23:39:14.643 回答
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

(这与阿德里安的答案基本相同,但我觉得这可以更好地解释它。他的答案最后还有 2 个多余的单引号。)

于 2018-03-02T04:53:27.843 回答
4

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

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

于 2020-06-14T16:25:03.613 回答
3

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

您正试图将太多东西塞进太小的空间,因此请使用 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
-------------
于 2015-10-27T00:36:01.420 回答
3
shell_escape () {
    printf '%s' "'${1//\'/\'\\\'\'}'"
}

实现说明:

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

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

  • 我们替换''\''

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

    1. '结束单引号

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

    3. '再次启动单引号

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

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

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

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

于 2019-04-25T20:27:09.110 回答
3

这是我的两分钱 - 如果一个人想要 -sh便携,而不仅仅是bash- 特定的(但是,解决方案不是太有效,因为它启动了一个外部程序 - sed):

  • 把它放在quote.sh(或只是quote)你的某处PATH
# 这适用于标准输入(stdin)
引用() {
  回声 -n "'" ;
  sed 's/\(['"'"']['"'"']*\)/'"'"'"\1"'"'"'/g' ;
  回声 -n "'"
}

案例“$1”在
 -) 引用 ;;
 *) echo "usage: cat ... | quote - # Bourne shell 的单引号输入" 2>&1 ;;
经社理事会

一个例子:

$ echo -n "G'day,伙计!" | ./quote.sh -
'G'"'"'今天,伙计!

而且,当然,这会转换回来:

$ echo 'G'"'"'今天,伙计!'
G'day,伙计!

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

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

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

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

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

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

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

(哦,也许我们想添加一个小帮助消息,这样脚本就不会在有人运行它时挂起,因为它./quote.sh --help想知道它做了什么。)

于 2020-01-31T10:39:52.693 回答
2

如果您在 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!'
于 2017-06-22T20:58:48.157 回答
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)。

于 2019-07-11T05:48:23.817 回答
2

需要一个最小的答案,这样人们就可以不用花费很多时间就可以开始工作,因为我不得不筛选那些雄辩的人。

没有办法转义单引号或单引号内的任何其他内容

以下是,也许令人惊讶的是,一个完整的命令:

$ echo '\'

其输出是:

\

甚至对于 bash 的长期用户来说,反斜杠在单引号内没有任何意义。也没有别的。

于 2021-12-30T10:59:51.360 回答
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"'

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

于 2015-11-21T11:01:15.963 回答
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'
于 2015-11-27T18:41:09.410 回答