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

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


当前回答

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

问题的作者没有注意到,在他最后一次尝试转义时,多了一个单引号('):

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

其他回答

由于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 like字符串。还要注意,“Bash Changes”文件(概述在这里)提到了很多与$'string'引用机制相关的更改和错误修复。

如何将一个特殊字符作为一个普通字符使用?它应该工作(与一些变化)在bash, zsh, mksh, ksh93和FreeBSD和busybox sh。

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

用单引号括住字符 (" ')保存的文字值 引号中的每个字符。一个 单引号之间不能出现 单引号,即使前面有 反斜杠。

所以bash不会理解:

别名x='y \'z '

但是,你可以用双引号括起来:

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

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


我经常在Perl脚本中创建一个“quotify”函数来为我做这件事。步骤如下:

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

这基本上解决了所有情况。

当您在shell脚本中引入eval时,生活会变得更加有趣。你必须重新对所有东西进行报价!

例如,创建一个名为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'\'''

(如果需要在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'\''\'\'''\'''\'''

这个函数:

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

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

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

您试图将太多内容塞进太小的空间,因此使用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
-------------