回显传递给带有可见引号的命令的确切命令行

echo the exact command-line passed to a command with visible quotes

所以我可以非常愉快地将命令行构建到 bash 数组中,然后用引号执行它并很好地引用每个参数:

declare -a cmd_args
cmd_args=("-p" "dir path/with spaces")
mkdir "${cmd_args[@]}"
echo dir*/*

但是我怎样才能以一种有意义的方式将其回显到屏幕上 - 即向用户显示一个他们随后可以键入的命令,或者我可以将其保存在日志文件中以供将来参考?所有这些看起来(基本上)都一样:

echo runnimg mkdir with arguments ${cmd_args[@]}
echo runnimg mkdir with arguments "${cmd_args[@]}"
echo "runnimg mkdir with arguments '${cmd_args[@]}'"
echo "runnimg mkdir with arguments '${cmd_args[*]}'"

==> runnimg mkdir with arguments '-p dir path/with spaces'

这显然是错误的命令。这并没有向用户显示他们可以键入的命令,或者我可以保存在日志文件中并在将来重现的命令。我想看:

runnimg mkdir with arguments '"-p" "dir path/with spaces"'

我考虑过使用 cat<<EOF:

cat<<EOF 
"${cmd_args[@]}" 
EOF

但实际上,这会在整个参数列表周围生成一个大引号!这里有什么?这曾经怎么可能是我的意图?如果是,那么我有 "${cmd_args[*]}".

这就是挑战。以用户可以说 "yes, that is the correct command" 的方式打印命令。

对那些说“%p\n”的人表示抱歉,虽然这对于日志文件来说可能没问题,但返回并格式化它仍然很痛苦 "unabiguously" 以重新测试命令, 它对于交互式 "this is the command feedback to the user" 来说还不够好。

也许已经有了答案,但如果是的话,它会被所有 "always quote your arguments" 类型的答案淹没。

如果您想打印每个参数都被引用的列表,无论引用是否合适:

printf '"%s" ' "${cmd_args[@]}"

如果您只想在需要时打印带引号或转义的参数列表:

printf '%q ' "${cmd_args[@]}"

做你的方式:

printf 'runnimg mkdir with arguments:'
printf ' %q' "${cmd_args[@]}"

A dash ksh POSIX shell 兼容实现:

#!/usr/bin/env dash

#cmd_args=("-p" "dir path/with spaces")
# dash and POSIX shell don't have arrays
# but support setting the arguments like this:
set -- -p "dir path/with spaces"
printf 'runnimg mkdir with arguments:'
# dash built-in printf does not know about %q format,
# but system command printf does.
env printf ' %q' "$@"
echo

mkdir "$@"

您可以使用 declare -p:

$ cmd_args=("-p" "dir path/with spaces")
$ declare -p cmd_args
declare -a cmd_args=([0]="-p" [1]="dir path/with spaces")

或使用 %q 格式化指令:

$ printf '%q\n' "${cmd_args[@]}"
-p
dir\ path/with\ spaces

基于BashFAQ/050

#!/bin/bash

trap 'printf RUNNING:\ %s\n "$BASH_COMMAND" >&2' DEBUG

foo () {
    printf '%s\n' "$@" > /dev/null
}

foo bar baz

foo 'qux bazinga' '1 2' 3 '4 5'

foo "I'm going home"

DEBUG 陷阱输出:

RUNNING: foo bar baz
RUNNING: foo 'qux bazinga' '1 2' 3 '4 5'
RUNNING: foo "I'm going home"

显示脚本中出现的引号。

函数foo设置为输出到/dev/null所以我们可以忽略它的输出。它只是您的脚本实际上可能是任何命令的替代品 运行.

这是一个将 trap 代码放在一个函数中并调用该函数来打开和关闭陷阱的版本。如果您只希望陷阱处理某些代码段,这很有用:

#!/bin/bash

dbt () {
    if [[  == on ]]
    then
        trap 'printf RUNNING:\ %s\n "$BASH_COMMAND" >&2' DEBUG
    elif [[  == off ]]
    then
        trap '' DEBUG
    else
        printf '%s\n' "Invalid action: "
    fi
}

foo () {
    printf '%s\n' "$@" > /dev/null
}

foo bar baz

dbt on

foo 'qux bazinga' '1 2' 3 '4 5'

foo "I'm going home"

now=$(date)

dbt off

declare -a array_b

dbt on

array_b=(a b 'c d' e)

输出:

RUNNING: foo 'qux bazinga' '1 2' 3 '4 5'
RUNNING: foo "I'm going home"
RUNNING: now=$(date)
RUNNING: dbt off
RUNNING: array_b=(a b 'c d' e)

另一种跟踪脚本执行的方法是使用 set -x.

Print a trace of simple commands, for commands, case commands, select commands, and arithmetic for commands and their arguments or associated word lists after they are expanded and before they are executed. The value of the PS4 variable is expanded and the resultant value is printed before the command and its expanded arguments.