Bash逃逸问题$@

Bash逃逸问题$@,bash,escaping,quotes,Bash,Escaping,Quotes,我编写了一个脚本来简化长启动命令的运行: # in ~/.bash_profile function runProgram() { sbt "run-main com.longpackagename.mainclass $@ arg3"; }; export -f runProgram; 但是,当我尝试传递多个参数时,它失败了: $ runProgram arg1 arg2 ... [info] Running com.longpackagename.mainclass arg1 sbt "

我编写了一个脚本来简化长启动命令的运行:

# in ~/.bash_profile
function runProgram() { sbt "run-main com.longpackagename.mainclass $@ arg3"; };
export -f runProgram;
但是,当我尝试传递多个参数时,它失败了:

$ runProgram arg1 arg2
...
[info] Running com.longpackagename.mainclass arg1
sbt "run-main com.longpackagename.mainclass ""arg1" "arg2"" arg3"
arg2和arg3发生了什么?他们是被bash还是sbt吃掉的

如果我按如下方式运行脚本,它将按预期工作:

$ runProgram "arg1 arg2"
--


另外:这类问题对我来说一直都在发生。我也希望能提供一份关于如何在bash中正确逃脱的参考资料。我尝试的&resources没有解决这种情况。

有关
bash
的最佳参考,包括如何引用,是bash手册本身,它几乎肯定安装在您的机器上,您可以通过键入
man bash
在没有互联网连接的情况下阅读它。这本书很值得一读,但没有真正的替代品

尽管如此,我将试图解释这一特定问题。有两件重要的事情需要知道:第一,bash如何(以及何时)将命令行拆分为单独的“字”(或命令行参数);第二,什么是
$@
$*
意思。这些并非完全无关

分词部分由特殊参数
IFS
控制,但我只提到了这一点;我想它没有被改变。有关更多详细信息,请参见
manbash

下面,我将使用双引号(
“…”
)引用字符串称为弱引用,使用撇号(
“…”
)引用字符串称为强引用。反斜杠(
\
)也是强引号的一种形式

发生分词:

  • 参数(外壳变量)替换为其值后

  • 只要有一系列空格字符

  • 除非以任何方式引用空格,(
    \
    是三种方式)

  • 在删除引号之前

  • 一旦一个命令被拆分为多个字,第一个字将用于查找要调用的程序或函数,其余的字将成为程序的参数。(我忽略了很多东西,比如shell元字符、重定向、管道等。有关更多详细信息,请参阅
    manbash

    如果参数的名称前面有一个
    $
    ,除非
    $name
    被强烈引用(即,
    '$name'
    或,例如,
    \$name
    ),否则将用它们的值替换参数(步骤1)。还有更多形式的参数替换。有关更多详细信息,请参见
    manbash

    现在,
    $@
    $*
    都表示“当前命令/函数的所有位置参数”,如果使用它们时不带引号,则它们的作用完全相同。它们将替换为所有位置参数,每个参数之间只有一个空格。由于这是一种类型的参数替换(如上所述),因此在替换之后会发生分词,除非替换在引号中,如上面的列表所示

    如果替换在引号中,则根据上述规则,插入参数之间的空白不受分词的影响。这正是
    $*
    的工作原理
    $*
    替换为空格分隔的命令行参数,结果是单词拆分<代码>“$*”替换为以空格分隔的命令行参数作为单个单词

    “$@”
    是一个例外。事实上,这就是
    $@
    存在的原因。如果
    $@
    位于弱引号(
    “$@”
    )内,则删除引号,并单独引用每个位置参数。然后,这些引用的位置参数被隔开并替换为
    $@
    。由于
    $@
    本身不再被引用,因此插入的空格确实会导致分词。最终的结果是,各个参数被保留为单个单词

    如果这还不完全清楚,这里有一个例子
    printf
    的优点是重复提供的格式,直到参数用完为止,这样可以很容易地看到发生了什么

    showargs() { 
      echo -n '$*:   '; printf "<%s> " $*; echo
      echo -n '"$*": '; printf "<%s> " "$*"; echo
      echo -n '"$@": '; printf "<%s> " "$@"; echo
    }
    
    showargs one two three
    showargs "one two" three
    
    为函数提供两个位置参数,以便
    $1
    arg1
    $2
    arg2

    首先,bash删除
    $@
    周围的引号。然而,它不能完全删除它们,因为那里也有引用的文本。因此,它必须关闭引用的文本,然后重新打开引用,从而产生:

    sbt "run-main com.longpackagename.mainclass "$@" arg3"
    
    现在,它可以在引用的分隔参数中替换:

    $ runProgram arg1 arg2
    ...
    [info] Running com.longpackagename.mainclass arg1
    
    sbt "run-main com.longpackagename.mainclass ""arg1" "arg2"" arg3"
    
    现在是单词拆分:

    sbt
    "run-main com.longpackagename.mainclass ""arg1"
    "arg2"" arg3"
    
    并删除引号:

    sbt
    run-main com.longpackagename.mainclass arg1
    arg2 arg3
    
    sbt
    只需要一个位置参数。你给了它两个,它忽略了第二个

    现在,假设使用单个参数调用函数,
    “arg1 arg2”
    。在这种情况下,替换
    $@
    会导致:

    sbt "run-main com.longpackagename.mainclass ""arg1 arg2"" arg3"
    
    分词会产生

    sbt
    "run-main com.longpackagename.mainclass ""arg1 arg2"" arg3"
    
    不加引号:

    sbt
    run-main com.longpackagename.mainclass arg1 arg2 arg3"
    

    对于
    sbt
    ,只有一个位置参数
    bash
    的最佳参考,包括如何引用,是bash手册本身,它几乎肯定安装在您的机器上,您可以通过键入
    man bash
    在没有互联网连接的情况下阅读它。这本书很值得一读,但没有真正的替代品

    尽管如此,我将试图解释这一特定问题。有两件重要的事情需要知道:第一,bash如何(以及何时)将命令行拆分为单独的“字”(或命令行参数);第二,什么是
    $@
    $*
    意思。这些并非完全无关

    分词部分由特殊参数
    IFS
    控制,但我只提到了这一点;我想它没有被改变。有关更多详细信息,请参见
    manbash

    下面,我将使用双引号(
    “…”
    )引用字符串称为弱引号,并使用撇号(
    “…”)引用字符串