【问题标题】:How to echo shell commands as they are executed如何在执行 shell 命令时回显它们
【发布时间】:2011-02-20 16:20:03
【问题描述】:

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

例如,给定以下行:

ls $DIRNAME

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

ls /full/path/to/some/dir

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

【问题讨论】:

标签: bash shell sh posix trace


【解决方案1】:

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

# 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 添加到两个脚本的顶部。
【解决方案2】:

对于 zsh,回显

setopt VERBOSE

对于调试,

setopt XTRACE

【讨论】:

    【解决方案3】:

    您可以使用 -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 也会做同样的事情,但没有变量插值
    • 这很好,但比我想要的更硬核。它似乎“下降”到我的顶级脚本运行的所有命令中。我真的只是希望我的顶级脚本的命令得到回显,而不是 bash 运行的所有内容。
    【解决方案4】:

    根据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
    

    【讨论】:

      【解决方案5】:

      为了允许回显复合命令,我使用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
      

      【讨论】:

        【解决方案6】:

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

        #!/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}" ; "$@" ; }
        • 我不喜欢这个答案。有很多边缘情况,你看到的不是你得到的(尤其是空格、引号、转义字符、变量/表达式替换等),所以不要盲目地将回显的命令粘贴到终端并假设它会运行以同样的方式。此外,第二种技术只是一种 hack,它会从您的命令中删除单词 eval 的其他实例。所以不要指望它在exe eval "echo 'eval world'"上正常工作!
        • 一个缺点是您不能在子shell 命令中使用这个exe 函数,例如:VAR=$(exe echo "hello world"); echo $VAR $VAR 的值将是回显的命令加上命令的结果。比如:'echo hello world hello world'
        【解决方案7】:

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

        bash -x foo.sh
        

        【讨论】:

          【解决方案8】:

          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)。
          • 太棒了;很好的答案。感谢您的洞察力。
          • 在我的question 中有人推荐了这个很酷的解决方案:superuser.com/questions/806599/…
          【解决方案9】:

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

          $ cat ./server
          #!/bin/bash -x
          ssh user@server
          
          $ ./server
          + ssh user@server
          user@server's password: ^C
          $
          

          【讨论】:

          • 请注意,./myScript 和 bash myScript 之间的工作方式似乎并不完全相同。还是要指出一件好事,谢谢。
          【解决方案10】:

          您还可以通过将它们包装在 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
          【解决方案11】:

          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(两者)。
          • 这很好用,但请注意“详细”会覆盖 $1
          【解决方案12】:

          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
          $
          

          【讨论】:

          • 如果您还想查看正在执行的编号行,请参阅stackoverflow.com/a/17805088/1729501
          • 如果我想在回显时为命令着色以区分命令及其结果输出怎么办?
          • @LewisChan :您可以添加彩色静态或动态前缀 f.e.时间戳,你的命令,见stackoverflow.com/a/62620480/2693875。
          • @AndreasDietrich 谢谢。如果你真的找到了实现我的笑话问题的方法,你会很开心。最好有一些强大的 cli 工具和几行代码。
          • bash -x foo.sh 是我需要的关键。 (作为评论发布,因为在不修改脚本本身的情况下,这是否会起作用尚不清楚;确实如此。)
          【解决方案13】:

          对于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.

          【讨论】:

          • 设置后如何禁用 echo/verbose?
          【解决方案14】:
          $ cat exampleScript.sh
          #!/bin/bash
          name="karthik";
          echo $name;
          
          bash -x exampleScript.sh
          

          输出如下:

          【讨论】:

          • 您的终端的颜色定义是什么(RGB,数字)?
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2015-01-03
          • 1970-01-01
          • 2015-12-09
          • 1970-01-01
          • 2018-01-24
          • 1970-01-01
          相关资源
          最近更新 更多