【发布时间】:2011-02-20 16:20:03
【问题描述】:
在 shell 脚本中,如何回显所有调用的 shell 命令并扩展任何变量名?
例如,给定以下行:
ls $DIRNAME
我希望脚本运行命令并显示以下内容
ls /full/path/to/some/dir
目的是保存所有调用的shell命令及其参数的日志。是否有更好的方法来生成这样的日志?
【问题讨论】:
在 shell 脚本中,如何回显所有调用的 shell 命令并扩展任何变量名?
例如,给定以下行:
ls $DIRNAME
我希望脚本运行命令并显示以下内容
ls /full/path/to/some/dir
目的是保存所有调用的shell命令及其参数的日志。是否有更好的方法来生成这样的日志?
【问题讨论】:
综合所有的答案,我发现这是最好的,最简单的
# https://stackoverflow.com/a/64644990/8608146
exe(){
set -x
"$@"
{ set +x; } 2>/dev/null
}
# example
exe go generate ./...
{ set +x; } 2>/dev/null 来自https://stackoverflow.com/a/19226038/8608146
如果需要命令的退出状态,如提到的here
使用
{ STATUS=$?; set +x; } 2>/dev/null
稍后使用$STATUS,如最后的exit $STATUS
稍微有用一点的
# https://stackoverflow.com/a/64644990/8608146
_exe(){
[ $1 == on ] && { set -x; return; } 2>/dev/null
[ $1 == off ] && { set +x; return; } 2>/dev/null
echo + "$@"
"$@"
}
exe(){
{ _exe "$@"; } 2>/dev/null
}
# examples
exe on # turn on same as set -x
echo This command prints with +
echo This too prints with +
exe off # same as set +x
echo This does not
# can also be used for individual commands
exe echo what up!
【讨论】:
#!/bin/bash 添加到两个脚本的顶部。
对于 zsh,回显
setopt VERBOSE
对于调试,
setopt XTRACE
【讨论】:
您可以使用 -x 选项在调试模式下执行 Bash 脚本。
这将回显所有命令。
bash -x example_script.sh
# Console output
+ cd /home/user
+ mv text.txt mytext.txt
您也可以将 -x 选项保存在脚本中。只需在 shebang 中指定 -x 选项即可。
######## example_script.sh ###################
#!/bin/bash -x
cd /home/user
mv text.txt mytext.txt
##############################################
./example_script.sh
# Console output
+ cd /home/user
+ mv text.txt mytext.txt
【讨论】:
bash -vx 也会做同样的事情,但没有变量插值
根据TLDP的Bash Guide for Beginners: Chapter 2. Writing and debugging scripts:
2.3.1.调试整个脚本
$ bash -x script1.sh...
现在有一个成熟的 Bash 调试器,可在SourceForge 获得。从 3.x 开始,大多数现代版本的 Bash 都提供这些调试功能。
2.3.2.调试部分脚本
set -x # Activate debugging from here w set +x # Stop debugging from here...
表 2-1。设置调试选项概述
Short | Long notation | Result
-------+---------------+--------------------------------------------------------------
set -f | set -o noglob | Disable file name generation using metacharacters (globbing).
set -v | set -o verbose| Prints shell input lines as they are read.
set -x | set -o xtrace | Print command traces before executing command.
...
或者,这些模式可以在脚本本身中指定,通过 将所需的选项添加到第一行 shell 声明。 选项可以组合,就像 UNIX 命令的情况一样:
#!/bin/bash -xv
【讨论】:
为了允许回显复合命令,我使用eval 加上 Soth 的 exe 函数来回显并运行命令。这对于管道命令很有用,否则只会显示不显示或仅显示管道命令的初始部分。
没有评估:
exe() { echo "\$ $@" ; "$@" ; }
exe ls -F | grep *.txt
输出:
$
file.txt
使用评估:
exe() { echo "\$ $@" ; "$@" ; }
exe eval 'ls -F | grep *.txt'
哪些输出
$ exe eval 'ls -F | grep *.txt'
file.txt
【讨论】:
我使用一个函数来回显并运行命令:
#!/bin/bash
# Function to display commands
exe() { echo "\$ $@" ; "$@" ; }
exe echo hello world
哪些输出
$ echo hello world
hello world
对于更复杂的命令管道等,可以使用eval:
#!/bin/bash
# Function to display commands
exe() { echo "\$ ${@/eval/}" ; "$@" ; }
exe eval "echo 'Hello, World!' | cut -d ' ' -f1"
哪些输出
$ echo 'Hello, World!' | cut -d ' ' -f1
Hello
【讨论】:
++ set +x 输出,并且看起来更干净。但是,对于一两个语句,bhassel 使用子shell 的回答是最方便的。
cp "foo bar" baz 和 cp foo "bar baz"。所以它有利于向用户显示进度信息;对于调试输出或记录可重现的命令则更少。不同的用例。在zsh 中,您可以使用:q 修饰符保留引用:exe() { echo '$' "${@:q}" ; "$@" ; }
eval 的其他实例。所以不要指望它在exe eval "echo 'eval world'"上正常工作!
VAR=$(exe echo "hello world"); echo $VAR $VAR 的值将是回显的命令加上命令的结果。比如:'echo hello world hello world'
在命令行中在 Bash 脚本名称之前键入“bash -x”。例如,要执行 foo.sh,请输入:
bash -x foo.sh
【讨论】:
shuckc's answer 用于回显选择行有一些缺点:您最终也会回显以下set +x 命令,并且您无法使用$? 测试退出代码,因为它被set +x.
另一种选择是在子shell中运行命令:
echo "getting URL..."
( set -x ; curl -s --fail $URL -o $OUTFILE )
if [ $? -eq 0 ] ; then
echo "curl failed"
exit 1
fi
这会给你这样的输出:
getting URL...
+ curl -s --fail http://example.com/missing -o /tmp/example
curl failed
不过,这确实会产生为命令创建新子 shell 的开销。
【讨论】:
++ set +x 输出的好方法。
if [ $? -eq 0 ] 替换为if (set -x; COMMAND)。
另一种选择是将“-x”放在脚本顶部而不是命令行:
$ cat ./server
#!/bin/bash -x
ssh user@server
$ ./server
+ ssh user@server
user@server's password: ^C
$
【讨论】:
./myScript 和 bash myScript 之间的工作方式似乎并不完全相同。还是要指出一件好事,谢谢。
您还可以通过将它们包装在 set -x 和 set +x 中来切换脚本中的选定行,例如,
#!/bin/bash
...
if [[ ! -e $OUT_FILE ]];
then
echo "grabbing $URL"
set -x
curl --fail --noproxy $SERV -s -S $URL -o $OUT_FILE
set +x
fi
【讨论】:
set +x
set -x 会给你你想要的。
这是一个示例 shell 脚本来演示:
#!/bin/bash
set -x #echo on
ls $PWD
这会扩展所有变量并在输出命令之前打印完整的命令。
输出:
+ ls /home/user/
file1.txt file2.txt
【讨论】:
set -o verbose 或set -v(仅“详细”)或set -o xtrace 或set -x(仅“xtrace”)或set -xv(两者)或set -o xtrace -o verbose(两者)。
set -x 或 set -o xtrace 扩展变量并在行前打印一个小 + 号。
set -v 或set -o verbose 不会在打印前扩展变量。
使用set +x和set +v关闭以上设置。
在脚本的第一行,可以将#!/bin/sh -x(或-v)放在脚本后面与set -x(或-v)产生相同的效果。
以上内容也适用于/bin/sh。
在 set attributes 和 debugging 上查看 bash-hackers 的 wiki。
$ cat shl
#!/bin/bash
DIR=/tmp/so
ls $DIR
$ bash -x shl
+ DIR=/tmp/so
+ ls /tmp/so
$
【讨论】:
bash -x foo.sh 是我需要的关键。 (作为评论发布,因为在不修改脚本本身的情况下,这是否会起作用尚不清楚;确实如此。)
对于csh 和tcsh,您可以set verbose 或set echo(或者您甚至可以同时设置,但大多数情况下可能会导致一些重复)。
verbose 选项打印几乎与您键入的完全相同的 shell 表达式。
echo 选项更能说明将通过生成执行的内容。
http://www.tcsh.org/tcsh.html/Special_shell_variables.html#verbose
http://www.tcsh.org/tcsh.html/Special_shell_variables.html#echo
Special shell variables
verbose
If set, causes the words of each command to be printed, after history substitution (if any). Set by the -v command line option.
echo
If set, each command with its arguments is echoed just before it is executed. For non-builtin commands all expansions occur before echoing. Builtin commands are echoed before command and filename substitution, because these substitutions are then done selectively. Set by the -x command line option.
【讨论】: