1120

在 shell 脚本中,如何回显所有调用的 shell 命令并扩展任何变量名?

例如,给定以下行:

ls $DIRNAME

我希望脚本运行命令并显示以下内容

ls /full/path/to/some/dir

目的是保存所有调用的 shell 命令及其参数的日志。是否有更好的方法来生成这样的日志?

4

14 回答 14

1284

set -xset -o xtrace扩展变量并在行前打印一个小 + 号。

set -vset -o verbose在打印前不展开变量。

使用set +xset +v关闭上述设置。

在脚本的第一行,可以将#!/bin/sh -x(or ) 放在后面的脚本中与( or -v) 具有相同的效果。set -x-v

以上也适用于/bin/sh.

请参阅 bash-hackers 的关于set属性调试的 wiki 。

$ cat shl
#!/bin/bash                                                                     

DIR=/tmp/so
ls $DIR

$ bash -x shl 
+ DIR=/tmp/so
+ ls /tmp/so
$
于 2010-05-18T00:06:07.993 回答
382

set -x会给你你想要的。

这是一个示例 shell 脚本来演示:

#!/bin/bash
set -x #echo on

ls $PWD

这会扩展所有变量并在命令输出之前打印完整的命令。

输出:

+ ls /home/user/
file1.txt file2.txt
于 2010-05-18T00:12:12.673 回答
114

我使用一个函数来回显并运行命令:

#!/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
于 2014-04-28T13:21:12.643 回答
97

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
于 2012-12-05T08:09:00.233 回答
74

shuckc回显选择行的答案有一些缺点:您最终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 的开销。

于 2013-10-15T22:34:03.807 回答
40

根据TLDPBash Guide for Beginners:第 2 章编写和调试脚本

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
于 2015-12-01T11:05:54.923 回答
38

另一种选择是将“-x”放在脚本顶部而不是命令行:

$ cat ./server
#!/bin/bash -x
ssh user@server

$ ./server
+ ssh user@server
user@server's password: ^C
$
于 2013-05-12T20:24:35.730 回答
29

您可以使用选项在调试模式下执行Bash 脚本。-x

这将回显所有命令。

bash -x example_script.sh

# Console output
+ cd /home/user
+ mv text.txt mytext.txt

您还可以在脚本中保存 -x 选项。只需-x在 shebang 中指定选项即可。

######## 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
于 2017-09-05T14:50:54.953 回答
22

在 Bash 脚本名称之前的命令行中键入“bash -x”。例如,要执行 foo.sh,请键入:

bash -x foo.sh
于 2014-03-15T17:40:39.733 回答
11

结合所有答案,我发现这是最好的,最简单的

# 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

如果需要命令的退出状态,这里提到

利用

{ 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!
于 2020-11-02T11:46:27.950 回答
4

对于 zsh,回显

setopt VERBOSE

而对于调试,

setopt XTRACE
于 2013-08-20T13:47:31.870 回答
2

为了允许回显复合命令,我使用evalplus 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
于 2015-01-27T17:49:27.643 回答
1

对于cshand tcsh,您可以set verboseset 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.

于 2014-06-29T22:27:22.417 回答
0
$ cat exampleScript.sh
#!/bin/bash
name="karthik";
echo $name;

bash -x exampleScript.sh

输出如下:

在此处输入图像描述

于 2016-02-10T17:04:05.287 回答