如何在执行shell命令时回显它们

时间:2010-05-18 00:03:10

标签: bash shell sh posix trace

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

例如,给定以下行:

ls $DIRNAME

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

ls /full/path/to/some/dir

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

15 个答案:

答案 0 :(得分:820)

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

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

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

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

以上内容也适用于/bin/sh

set attributesdebugging上查看bash-hackers的维基。

$ cat shl
#!/bin/bash                                                                     

DIR=/tmp/so
ls $DIR

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

答案 1 :(得分:273)

set -x会给你你想要的东西。

以下是一个示例shell脚本:

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

ls $PWD

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

输出:

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

答案 2 :(得分:79)

您还可以通过将其包裹在set -xset +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

答案 3 :(得分:71)

我使用函数回显然后运行命令

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

答案 4 :(得分:46)

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的开销。

答案 5 :(得分:32)

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

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

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

(对于所选答案的评论不足。)

答案 6 :(得分:19)

根据TLDPBash Guide for Beginners: Chapter 2. Writing and debugging scripts

  

<强> 2.3.1。调试整个脚本

$ bash -x script1.sh
     

...

     

现在有一个完整的Bash调试器,可在SourceForge获得。大多数现代版本的Bash都提供这些调试功能,从3.x开始。

     

<强> 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

答案 7 :(得分:17)

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

bash -x foo.sh

答案 8 :(得分:8)

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



答案 9 :(得分:5)

上面发布的人:

#!/bin/bash
#function to display commands
exe() { echo "\$ $@" ; "$@" ; }

这看起来很有希望,但我不能为我的生活弄清楚它的作用。我在man bash页面搜索并搜索&#34; \ $&#34;和&#34; $ @&#34;,并且绝对找不到没有

我理解正在创建一个函数,名为&#34; exec()&#34;。我理解花括号标记了函数的开头和结尾。我想我明白,分号标志着&#34;艰难的回归&#34;在多行命令之间,以便&#39; {echo&#34; \ $ $ @&#34; ; &#34; $ @&#34; ; }&#39;变本质上:

{
echo "\$ $@"
"$@"

}

任何人都可以给我一个简短的解释,或者在哪里可以找到这些信息,因为很明显我的google-fu让我失望了吗?

(没有意义在旧线程上开始一个新问题,我的目标是将输出重新路由到文件。&#34; set -x; [commands]; set + x&#34;方法可以正常工作对我来说很好,但我无法弄清楚如何将结果回显到文件而不是屏幕,所以我试图理解这个其他方法,希望我能用我对重定向/管道/三通的理解很差/ etc做同样的事情。)

谢谢!

LATER EDIT:

经过一些修补,我相信我弄明白了。这是我所需要的等效代码:

SCRIPT_LOG="\home\buddy\logfile.txt"
exe () {
  params="$@"                       # Put all of the command-line into "params"
  printf "%s\t$params" "$(date)" >> "$SCRIPT_LOG"   # Print the command to the log file
  $params                           # Execute the command
}

exe rm -rf /Library/LaunchAgents/offendingfile
exe rm -rf /Library/LaunchAgents/secondoffendingfile

logfile.txt中的结果类似于:

Tue Jun  7 16:59:57 CDT 2016  rm -rf /Library/LaunchAgents/offendingfile
Tue Jun  7 16:59:57 CDT 2016  rm -rf /Library/LaunchAgents/secondoffendingfile

正是我需要的。谢谢!

答案 10 :(得分:3)

对于zsh echo

 setopt VERBOSE

和调试

 setopt XTRACE

答案 11 :(得分:2)

对于cshtcsh,您可以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.

答案 12 :(得分:1)

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

没有评估:

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

答案 13 :(得分:1)

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

bash -x exampleScript.sh

输出如下:

enter image description here

答案 14 :(得分:1)

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

exe(){
    set -x
    "$@"
    { set +x; } 2>/dev/null
}
# example
exe go generate ./...

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