回显用于启动 Bash 脚本的确切命令

回显用于启动 Bash 脚本的确切命令

我想向 bash 脚本的用户提供类似这样的反馈:“您已运行以下命令 <用户输入的确切命令>,最好像这样运行它 <更好的命令>”。出于可用性原因,我认为最好尽可能精确地打印用户输入的命令。

可以做的事情之一是这样的:

echo "You've run the following command:" ${0} "${@}"

但这不会保留引号或转义文本。可以检查“${@}”并查看哪些条目需要转义或引号,但脚本无法分辨用户使用了其中哪一个,并重现它。

history无权访问运行该脚本的 shell 中的最后一个命令(而且,出于充分的理由,它在脚本中默认不启用)。

问题:尽可能精确地重现用户输入的运行脚本的命令的最佳方法是什么?

答案1

您无法获取未解析的命令行;shell 接收命令行并在启动代码之前对其进行解析。(当然,假设有一个 shell。但不一定需要有一个。)

示例。将其另存为args,并使其可执行(chmod a+x args):

#!/bin/sh
echo "0=$0, #=$#, *=($*)"               # Program, number of args, list of args
printf '> %s\n' "$@"                    # Each arg in turn

你可以用任何一种方式调用它,并且结果输出将是相同的

./args 'one two' three
a='one two'; ./args "$a" three          # Notice $a is quoted so remains one arg
./args 'one'" two" three                # Quotes are stripped by the shell
a=one b=; ./args "$a two" $b three      # I really dislike unquoted variables
a=one; ./args "$a two" three            # Variations on a quoted theme
./args one\ two three                   # And more

您所能期望的最好结果是"$@"根据需要使用和引用参数。如果您有来自 Frodo Looijaard 的非标准getopt(不是getopts),util‐linux您可以使用几行这样的代码来获取引用的字符串:

line=$(getopt --shell sh '' -- "$@")    # Guess the command line
printf 'Command: %s %s\n' "$0" "${line# -- }"

但它仍然没有完美地再现原始的命令行。

答案2

没有固定的方法。只有父 shell 知道使用的确切引用;但父 shell 不一定是 shell,它可以是一个进程,它为脚本提供参数数组,而无需解析需要引用或转义的表示。如果它是 shell,则没有标准接口允许您的脚本获取有关引用的信息。

“用户输入的命令”可能只是shell_function./the-script "${array[@]}",因此如果您想“尽可能精确地”复制它,那么不仅仅是引用。

如果父 shell 足够复杂,则可能可以对其进行配置预先,这样你的脚本就可以获取有关命令原始形式的信息。这会很麻烦,而且依赖于 shell(首先请参阅echo-literally 这里)。如果我是你的脚本的用户,我不会只想让你的脚本看起来很智能而配置我的交互式 shell。

Windows 中的程序可以用引号知道它的命令字符串(参见另一个答案其中说“情况有些复杂”),但我不认为 Bash 脚本即使在 Windows 中启动时也可以使用这个“功能”。

您能做的最好的就是您所描述的:

可以检查“${@}”并查看哪些条目需要转义或引用,但脚本无法判断用户使用了哪一个,并重现它。

实际上,你可以使用以下方法轻松添加引号"${0@Q}""${@@Q}"。 例子:

foo=bar' 'baz
bash -c '
echo "You have run the following command: ${0@Q} ${@@Q}"
' "arbitrary name" arg1 arg\ 2 "arg'3"          'arg 4' "a r"\ g'\5' "$foo"

输出:

You have run the following command: 'arbitrary name' 'arg1' 'arg 2' 'arg'\''3' 'arg 4' 'a r g\5' 'bar baz'

传递后的 shell 代码-c不仅无法分辨出所使用的确切引用,也无法知道它foo曾经存在过。

相关内容