Ale*_*min 9 bash shell-script quoting arguments
在 shell 脚本中,我的理解是"$@"扩展到脚本参数,根据需要引用它们。例如,这将脚本参数转发给 gcc:
gcc -fPIC "$@"
Run Code Online (Sandbox Code Playgroud)
但是,当使用 bash pass-to-stdin 语法时<<<,"@$"它不会像我期望的那样工作。
#!/bin/bash
cat <<< "$@"
Run Code Online (Sandbox Code Playgroud)
调用脚本./test.sh foo "bar baz"给出
foo bar baz
Run Code Online (Sandbox Code Playgroud)
我希望
foo "bar baz"
Run Code Online (Sandbox Code Playgroud)
有没有办法编写一个shell脚本来打印它的参数,就像你在shell提示符下写的那样?例如:关于接下来要使用什么命令的提示,包括提示中的脚本参数。
好吧,"$@"扩展到位置参数列表,每个位置参数一个参数。
当你这样做时:
set '' 'foo bar' $'blah\nblah'
cmd "$@"
Run Code Online (Sandbox Code Playgroud)
cmd正在使用这 3 个参数调用:空字符串foo bar和blah<newline>blah. shell 将使用以下内容调用execve()系统调用:
execve("/path/to/cmd", ["cmd", "", "foo bar", "blah\nblah"], [envvars...]);
Run Code Online (Sandbox Code Playgroud)
如果您想重建一个可以重现相同调用的 shell 命令行(即 shell 语言中的代码),您可以执行以下操作:
awk -v q="'" '
function shellquote(s) {
gsub(q, q "\\" q q, s)
return q s q
}
BEGIN {
for (i = 1; i < ARGC; i++) {
printf "%s", sep shellquote(ARGV[i])
sep = " "
}
printf "\n"
}' cmd "$@"
Run Code Online (Sandbox Code Playgroud)
或者使用zsh,请求不同类型的引号:
$ set '' 'foo bar' $'blah\nblah'
$ print -r -- cmd "${(q)@}"
cmd '' foo\ bar blah$'\n'blah
$ print -r -- cmd "${(qq)@}"
cmd '' 'foo bar' 'blah
blah'
$ print -r -- cmd "${(qqq)@}"
cmd "" "foo bar" "blah
blah"
$ print -r -- cmd "${(qqqq)@}"
cmd $'' $'foo bar' $'blah\nblah'
Run Code Online (Sandbox Code Playgroud)
或与zsh,bash或ksh93(此处为bash, YMMV 与其他外壳):
$ set '' 'foo bar' $'blah\nblah'
$ printf cmd; printf ' %q' "$@"; printf '\n'
cmd '' foo\ bar $'blah\nblah'
Run Code Online (Sandbox Code Playgroud)
您还可以使用 shell 的 xtrace 选项,使 shell 打印将要执行的内容:
$ (PS4=; set -x; : cmd "$@")
: cmd '' 'foo bar' 'blah
blah'
Run Code Online (Sandbox Code Playgroud)
上面,我们:使用cmd位置参数作为参数运行了no-op 命令。我的外壳以适合重新输入外壳的漂亮引用方式打印它们。并非所有 shell 都这样做。
Run Code Online (Sandbox Code Playgroud)`"$@"` expands to the script arguments, quoting them as needed
不,这不是发生的事情。调用程序需要一个参数列表,每个参数都是一个字符串。当您运行shell程序./test.sh foo "bar baz",这将构建三个参数调用:./test.sh,foo,和bar baz。(第零个参数是程序名称;这允许程序知道它们的名称是什么。)引用是 shell 的一个特性,而不是程序调用的一个特性。shell 在进行调用时会构建此列表。
"$@"直接将传递给脚本或函数的参数列表复制到使用它的调用中的参数列表。由于没有对这些列表进行 shell 解析,因此不涉及引用。
在 中cat <<< "$@",您在"$@"需要单个字符串的上下文中使用。该<<<operator`需要一个字符串,而不是字符串列表。在这种情况下,bash 获取列表中的元素,并用中间的空格将它们连接起来。
对于脚本调试,如果您运行set -x(set +x以关闭) ,则会激活跟踪模式,其中在执行每个命令之前打印每个命令。在 bash 中,该跟踪带有引号,可以将命令粘贴回 shell(并非每个sh实现都如此)。
如果您有一个字符串并且您想将其转换为解析回原始字符串的 shell 源语法,您可以用单引号将其括起来,并将字符串中的每个单引号替换为'\''.
for x do
printf %s "'${x//\'/\'\\\'\'}' "
done
echo
Run Code Online (Sandbox Code Playgroud)
字符串替换语法是特定于 ksh93/bash/zsh/mksh 的。在普通 sh 中,您需要遍历字符串。
for raw do
quoted=
while case "$raw" in *\'*) true;; *) false;; esac; do
quoted="$quoted'\\''${raw%%\'*}"
raw="${raw#*\'}"
done
printf %s "'$quoted$raw' "
done
echo
Run Code Online (Sandbox Code Playgroud)