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

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


当前回答

我并没有特别提到引用问题,因为,有时候,考虑另一种方法是合理的。

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

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

其他回答

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

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

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

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'

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

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

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

我并没有特别提到引用问题,因为,有时候,考虑另一种方法是合理的。

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

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