从脚本中传递包含空格的字符串作为命令行参数

Lab*_*dor 3 shell scripting bash quoting

我在 mac 上使用 bash shell。

我想编写一个 shell 脚本 'gac' 以便运行

> gac one two three
Run Code Online (Sandbox Code Playgroud)

产生与跑步完全相同的效果

> git add .
> git commit -m "one two three"
Run Code Online (Sandbox Code Playgroud)

到目前为止,我的脚本是这样的:

function gac() {
    git add .
    concatenated="'$*'"
    git commit -m "$concatenated"
}
Run Code Online (Sandbox Code Playgroud)

我发现伎俩拼接外壳参数为单个空格分隔字符串(脚本的第二行)在这里。上面的脚本几乎可以工作,除了日志消息读取

a382806 'one two three'
Run Code Online (Sandbox Code Playgroud)

当我使用我的 shell 脚本时,而不是

a382806 one two three
Run Code Online (Sandbox Code Playgroud)

就像我手动输入时一样

> git commit -m "one two three"
Run Code Online (Sandbox Code Playgroud)

在命令行上。

有任何想法吗?

Eli*_*gan 9

当您在扩展(例如参数扩展)周围放置双引号时,扩展文本不受分词的影响。(这是在扩展周围使用双引号的原因之一;另一个是为了防止globbing。)此外,在双引号内,单引号没有被特殊处理,因此它们不会执行引用并且不会被删除 $*$

所以,正如Michael Homer 所说,你可以省略虚假 ' '标记,你的函数应该可以工作。我建议这样写:

gac() {
    git add .
    git commit -m "$*"
}
Run Code Online (Sandbox Code Playgroud)

您可以使用function关键字在 Bash 中定义函数,但上面显示的语法同样有效,并且可以在 Bourne 风格的 shell 中移植。


为了更直接地解决这里的概念问题,原始代码中的这一行表明您想要扩展其值包含空格的参数,这样您就可以将其作为单个参数传递给git

    concatenated="'$*'"
Run Code Online (Sandbox Code Playgroud)

当您自己编写包含空格的单个参数时,您会引用空格,通常用引号将整个字符串括起来。该行中的' ' inside的存在" "告诉我您试图包含通常键入的引号。

这种方法不起作用的原因是外壳本身就是解释您的引号的原因;它们通常对您从 shell 运行的命令没有任何意义。假设您有以下命令:

some-command 'foo bar' baz
Run Code Online (Sandbox Code Playgroud)

该命令实际上并未向该some-command命令传递任何引号。取而代之的是,它运行some-commandfoo bar作为参数1和baz作为参数2。(此外,还有一个参数0,它告诉程序它是如何运行;所述外壳通过some-command该)。

使用引号可以告诉 shell参数的开始和结束位置。空格通常会告诉 shell 将每一侧的文本视为单独的参数1,但您想抑制空格对 shell 的特殊含义,这就是引用的作用。当引号被引用时,就像内部' 'inside 一样" ",也会删除它们的特殊含义。然后他们不执行引用,而是按字面传递给您的命令,git如其日志所示:

a382806 'one two three'
Run Code Online (Sandbox Code Playgroud)

1 在 shell 的操作中,空格和制表符以两种相关但不同的方式将文本分成单独的单词。首先,当最初解析命令时,未加引号的空格和制表符将词法标记分开。其他元字符也可以这样做,但它们具有额外的效果——例如,;将一行分成多个命令。其次,在未加引号的上下文中执行由2表示的参数扩展或任何其他shell 扩展时,结果会立即进行分使用字符 in作为分隔符。该默认值$$IFSIFS 是一个空格后跟一个制表符,后跟一个换行符。

2命令替换,即使使用` `语法代替$( )语法。