繁体   English   中英

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

[英]How to escape single quotes within single quoted strings

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

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

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

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

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


我经常在我的 Perl 脚本中创建一个“引用”函数来为我做这件事。 步骤是:

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

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

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

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

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

所以 bash 不会理解:

alias x='y \\'z '

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

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

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

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

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

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

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

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

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

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

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

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

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

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

alias rxvt=$command

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

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

不可能。

如果我们假设我们正在使用 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"

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

如果使用类似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. 双引号示例,其中"十六进制\\x22八进制\\042 (其对应的ASCII 码)进行转义。

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

# 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

另一种选择:这允许在同一个 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)")'

注意:对于我~/.bash_aliases文件中gs_set_title bash 函数,请参阅我的其他答案

参考:

  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 字符? .

这些答案中的大多数都针对您要询问的具体案例。 我和朋友开发了一种通用方法,允许任意引用,以防您需要通过多层 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的单个参数是必要的。)它可以更普遍地用于通过多层扩展正确引用:

$ 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分成两个单独的命令, quote_args cd /pwd 1>&2 ,所以执行pwd命令时 CWD 仍然是/tmp 第二个示例说明了类似的通配问题。 实际上,所有 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

等等。

考虑到诸如successsbinpwd之类的词不需要在 shell 中引用,这些示例可能看起来有些过头了,但是在编写接受任意输入的脚本时要记住的关键点是,您要引用所有不是绝对的内容当然不需要引用,因为你永远不知道用户什么时候会抛出Robert'; rm -rf / 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

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

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

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

有时我会在 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"'

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

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

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

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

另一种解决嵌套引用层数过多问题的方法:<\/strong>

您正试图将太多东西塞进太小的空间,因此请使用 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
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 始终编码为单引号字符串,因为它们比双引号字符串简单得多。

这是我的两分钱 - 如果一个人想要sh<\/code> -portable,而不仅仅是bash<\/code> -specific(但是,解决方案不是太有效,因为它启动了一个外部程序 - sed<\/code> ):

  • 把它放在你的PATH<\/code>某处的quote.sh<\/code> (或只是quote<\/code> )中:<\/li><\/ul>
     # 这适用于标准输入(stdin)\n引用() {\n 回声 -n "'" ;\n  sed 's\/\\(['"'"']['"'"']*\\)\/'"'"'"\\1"'"'"'\/g' ;\n 回声 -n "'"\n }\n\n案例“$1”在\n  -) 引用 ;;\n  *) echo "usage: cat ... | quote - # Bourne shell 的单引号输入" 2>&1 ;;\n经社理事会\n\n<\/pre>

    一个例子:

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

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

     $ echo 'G'"'"'今天,伙计!'\n G'day,伙计!\n<\/pre>

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

    一种标准方法是将sed<\/code>与以下替换命令一起使用:

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

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

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

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

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

如果你安装了 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 )。

这个功能:

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

这是另一个解决方案。 该函数将接受一个参数并使用单引号字符适当地引用它,正如上面投票答案所解释的那样:

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

需要一个最小的答案,这样人们就可以不用花费大量时间就可以开始工作,因为我不得不筛选在其他地方雄辩的人。

没有办法<\/strong>转义单引号或单引号内的任何其他内容<\/em>。

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

$ echo '\'

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM