在一个shell脚本,我如何回显所有shell命令调用和展开任何变量名?

例如,给定以下一行:

ls $DIRNAME

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

ls /full/path/to/some/dir

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


Set -x或Set -o xtrace展开变量并在行前打印一个小的+号。

Set -v或Set -o verbose不会在打印之前展开变量。

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

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

上面的代码也适用于/bin/sh。

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

$ cat shl
#!/bin/bash                                                                     

DIR=/tmp/so
ls $DIR

$ bash -x shl 
+ DIR=/tmp/so
+ ls /tmp/so
$

Set -x会得到你想要的。

下面是一个示例shell脚本来演示:

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

ls $PWD

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

输出:

+ ls /home/user/
file1.txt file2.txt

您还可以通过将脚本中的选定行包装在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

另一种选择是在你的脚本顶部放置"-x",而不是在命令行上:

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

$ ./server
+ ssh user@server
user@server's password: ^C
$

对于zsh,使用echo

setopt VERBOSE

为了调试,

setopt XTRACE

Shuckc对回显选择行的回答有一些缺点:您最终也会回显以下set +x命令,并且您失去了使用$?因为它被集合+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的开销。


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

bash -x foo.sh

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

#!/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

对于csh和tcsh,可以设置verbose或设置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


特殊的壳变量

详细的 如果设置,则导致在历史替换(如果有)之后打印每个命令的单词。由-v命令行选项设置。

回声 如果设置了,每个命令及其参数都会在执行之前被回显。对于非内置命令,所有展开都发生在回显之前。内置命令在命令和文件名替换之前被回显,因为这些替换是选择性地完成的。由-x命令行选项设置。


为了允许复合命令被回显,我使用eval加上Soth的exe函数来回显和运行命令。这对于管道命令非常有用,否则这些管道命令只显示不显示或只显示管道命令的初始部分。

没有eval:

exe() { echo "\$ $@" ; "$@" ; }
exe ls -F | grep *.txt

输出:

$
file.txt

eval:

exe() { echo "\$ $@" ; "$@" ; }
exe eval 'ls -F | grep *.txt'

的输出

$ exe eval 'ls -F | grep *.txt'
file.txt

根据TLDP的Bash初学者指南:第2章。编写和调试脚本:

2.3.1. 调试整个脚本 $ bash -x script1.sh ... 现在在SourceForge上有一个成熟的Bash调试器。从3.x开始,这些调试特性在大多数现代版本的Bash中都可以使用。 2.3.2. 调试脚本的一部分 set -x #从这里激活调试 w set +x #从这里停止调试 ... 表2 - 1。set调试选项概述

    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十五


$ cat exampleScript.sh
#!/bin/bash
name="karthik";
echo $name;

bash -x exampleScript.sh

输出结果如下:


可以使用-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

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

#!/bin/bash
# https://stackoverflow.com/a/64644990/8608146
exe(){
    set -x
    "$@"
    { set +x; } 2>/dev/null
}
# example
exe go generate ./...

{set +x;} 2>/dev/null from https://stackoverflow.com/a/19226038/8608146

如果需要命令的退出状态,如这里所述

Use

{ STATUS=$?; set +x; } 2>/dev/null

然后使用$STATUS,比如在最后退出$STATUS

一个稍微有用一点的

#!/bin/bash
# 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!