在字符串中使用 `$@` 参数时如何将它们分开?

how to keep `$@` arguments seperated when using them in a string?

嘿,我正在为 git 编写一个包装脚本,它将一个 git 命令应用于它的所有子模块,例如:supergit commit -m "change message" 提交给所有子模块。
该脚本主要执行以下操作:

function git_foreach () {
    git submodule foreach "git \"$@\" || : "
}

git_foreach "$@"

问题是当 supergit 调用包含一个带有 spaces 的参数时(就像在上面的提交消息中一样),space 分隔的调用被解释为多个参数。 我在这个 answer 中读到,这样做的方法是使用 "$@" 但这在字符串中不起作用。

有没有办法扩展 $@ 以保留引号,以便我的函数按预期工作?

编辑:
我想要的是将参数传递给 git submodule foreach,用 supergit commit -m "commit message" 我想 运行:
git submodule foreach "git commit -m \"commit message\""

如果您的 /bin/sh 由 Bash

提供

printf %q 将生成一个正确转义的数据版本,以便由 shell.

解析

printf '%q ' "$@" 为数组中的每个参数生成一个包含单独 shell-escaped 单词的字符串,每个单词后有空格。

因此:

git_foreach() {
    local cmd_q
    printf -v cmd_q '%q ' "$@"
    git submodule foreach "git $cmd_q ||:"
}

...或者,使用 bash 5.0 或更新版本(增加了 ${var@Q} 扩展),我们可以制作这一行:

git_foreach() { git submodule foreach "git ${@@Q} ||:"; }

对于 printf %q 的当前实现,git_foreach commit -m "commit message" 调用 git submodule foreach 'git commit -m commit\ message' 或语义等价物;转义与您手写的方式不同,但命令的效果完全相同。


如果您需要更广泛的兼容性

不幸的是,printf %q 和较新的 ${variable@Q} 扩展都能够生成使用 bash-only 语法的字符串(尤其是当您的字符串包含换行符、制表符或类似内容时)。如果您不控制 shell git 开始 运行 foreach 命令,那么我们需要生成一个字符串,该字符串被转义以供任何 POSIX-compliant 使用shell.

Bash 没有这样做的功能...但是 Python 有!

posix_escape() {
  python3 -c 'import sys, shlex; print(" ".join([shlex.quote(s) for s in sys.argv[1:]]))' "$@"
}

git_foreach() {
  local cmd_q
  cmd_q=$(posix_escape "$@")
  git submodule foreach "git $cmd_q ||:"
}