有没有办法让bash进入一种冗长的模式,这样,当它运行一个shell脚本时,它会在运行它之前回显它将要运行的命令?也就是说,可以看到运行的命令(以及它们的输出),类似于make
的输出?
也就是说,如果运行像
这样的shell脚本echo "Hello, World"
我想要以下输出
echo "Hello, World"
Hello, World
或者,是否可以编写一个名为echo_and_run
的bash函数来输出命令然后运行它?
$ echo_and_run echo "Hello, World"
echo "Hello, World"
Hello, World
答案 0 :(得分:31)
您可以在调用echo
之前为eval
命令创建自己的函数。
Bash也有调试功能。一旦set -x
bash在执行之前显示每个命令。
cnicutar@shell:~/dir$ set -x
cnicutar@shell:~/dir$ ls
+ ls --color=auto
a b c d e f
答案 1 :(得分:17)
要回答问题的第二部分,这里有一个shell函数可以执行您想要的操作:
echo_and_run() { echo "$*" ; "$@" ; }
我使用类似的东西:
echo_and_run() { echo "\$ $*" ; "$@" ; }
在命令前打印$
(它看起来像一个shell提示符,并且更清楚它是一个命令)。当我想要显示它执行的一些(但不是全部)命令时,我有时会在脚本中使用它。
正如其他人所说,它确实失去了引号:
$ echo_and_run echo "Hello, world"
$ echo Hello, world
Hello, world
$
但我不认为有任何好办法可以避免这种情况;在echo_and_run
有机会看到它们之前,shell会引用引号。您可以编写一个脚本来检查包含空格和其他shell元字符的参数,并根据需要添加引号(它仍然不一定与您实际键入的引号相匹配)。
答案 2 :(得分:7)
可以将bash的printf
与%q
格式说明符结合使用来转义参数,以便保留空格:
function echo_and_run {
echo "$" "$@"
eval $(printf '%q ' "$@") < /dev/tty
}
答案 3 :(得分:0)
要添加到其他人的实现,这是我的基本脚本样板,包括参数解析(如果您要切换详细级别,这很重要)。
#!/bin/sh
# Control verbosity
VERBOSE=0
# For use in usage() and in log messages
SCRIPT_NAME="$(basename $0)"
ARGS=()
# Usage function: tells the user what's up, then exits. ALWAYS implement this.
# Optionally, prints an error message
# usage [{errorLevel} {message...}
function usage() {
local RET=0
if [ $# -gt 0 ]; then
RET=$1; shift;
fi
if [ $# -gt 0 ]; then
log "[$SCRIPT_NAME] ${@}"
fi
log "Describe this script"
log "Usage: $SCRIPT_NAME [-v|-q]" # List further options here
log " -v|--verbose Be more verbose"
log " -q|--quiet Be less verbose"
exit $RET
}
# Write a message to stderr
# log {message...}
function log() {
echo "${@}" >&2
}
# Write an informative message with decoration
# info {message...}
function info() {
if [ $VERBOSE -gt 0 ]; then
log "[$SCRIPT_NAME] ${@}"
fi
}
# Write an warning message with decoration
# warn {message...}
function warn() {
if [ $VERBOSE -gt 0 ]; then
log "[$SCRIPT_NAME] Warning: ${@}"
fi
}
# Write an error and exit
# error {errorLevel} {message...}
function error() {
local LEVEL=$1; shift
if [ $VERBOSE -gt -1 ]; then
log "[$SCRIPT_NAME] Error: ${@}"
fi
exit $LEVEL
}
# Write out a command and run it
# vexec {minVerbosity} {prefixMessage} {command...}
function vexec() {
local LEVEL=$1; shift
local MSG="$1"; shift
if [ $VERBOSE -ge $LEVEL ]; then
echo -n "$MSG: "
local CMD=( )
for i in "${@}"; do
# Replace argument's spaces with ''; if different, quote the string
if [ "$i" != "${i/ /}" ]; then
CMD=( ${CMD[@]} "'${i}'" )
else
CMD=( ${CMD[@]} $i )
fi
done
echo "${CMD[@]}"
fi
${@}
}
# Loop over arguments; we'll be shifting the list as we go,
# so we keep going until $1 is empty
while [ -n "$1" ]; do
# Capture and shift the argument.
ARG="$1"
shift
case "$ARG" in
# User requested help; sometimes they do this at the end of a command
# while they're building it. By capturing and exiting, we avoid doing
# work before it's intended.
-h|-\?|-help|--help)
usage 0
;;
# Make the script more verbose
-v|--verbose)
VERBOSE=$((VERBOSE + 1))
;;
# Make the script quieter
-q|--quiet)
VERBOSE=$((VERBOSE - 1))
;;
# All arguments that follow are non-flags
# This should be in all of your scripts, to more easily support filenames
# that start with hyphens. Break will bail from the `for` loop above.
--)
break
;;
# Something that looks like a flag, but is not; report an error and die
-?*)
usage 1 "Unknown option: '$ARG'" >&2
;;
#
# All other arguments are added to the ARGS array.
*)
ARGS=(${ARGS[@]} "$ARG")
;;
esac
done
# If the above script found a '--' argument, there will still be items in $*;
# move them into ARGS
while [ -n "$1" ]; do
ARGS=(${ARGS[@]} "$1")
shift
done
# Main script goes here.
...后来
vexec 1 "Building myapp.c" \
gcc -c myapp.c -o build/myapp.o ${CFLAGS}
注意:这不会涵盖管道命令;你需要bash -c那些东西,或者把它们分解成中间变量或文件。
答案 4 :(得分:0)
可以在脚本或交互式会话中添加到bash
命令行或通过set
命令的两个有用的shell选项:
- -v 在读取时打印shell输入行。
- -x 展开每个简单命令
for
命令,case
命令,select
命令或算术for
命令后,显示PS4
的扩展值,后跟命令及其扩展 参数或相关单词列表。
答案 5 :(得分:0)
有关额外的时间戳和I / O信息,请考虑来自 Debian 的 devscripts 包中的annotate-output
命令:
annotate-output echo hello
输出:
13:19:08 I: Started echo hello
13:19:08 O: hello
13:19:08 I: Finished with exitcode 0
现在查找不存在的文件,并注意 STDERR 输出的 E::
annotate-output ls nosuchfile
输出:
13:19:48 I: Started ls nosuchfile
13:19:48 E: ls: cannot access 'nosuchfile': No such file or directory
13:19:48 I: Finished with exitcode 2
答案 6 :(得分:-1)
使用下面提到的简单shell脚本创建名为“echo_and_run”的可执行(+ x)基本脚本!
#!/bin/bash
echo "$*"
$@
$ ./echo_and_run“echo Hello,World”
echo Hello, World
Hello, World
或者只使用一个 bash函数。
echo_and_run() { echo "\$ $*" ; "$@" ; }
甚至这种变体也可以起作用:
function echo_and_run {
echo "$" "$@"
eval $(printf '%q ' "$@") < /dev/tty
}
然而, cnicutar的方法set -x
是可靠的,强烈建议。