如何在执行时输出Shell命令

1269
在一个shell脚本中,如何输出所有被调用的shell命令并展开所有变量名?例如,给定以下行:
ls $DIRNAME

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

ls /full/path/to/some/dir

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


另请参阅(重复):如何在执行之前打印每个命令? - Gabriel Staples
17个回答

8
对于zsh来说,echo命令如下:
setopt VERBOSE

作为调试工具,

setopt XTRACE

谢谢!这很有趣。 verbose 可以小写。要移除它,使用 unsetopt 而不是 setopt。例如:unsetopt verbose。附注:这在脚本中无效。 - ADTC

2
为了让复合命令能够被显示出来,我使用了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

1
对于csh和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


特殊的shell变量

verbose 如果设置了,会导致每个命令的单词在历史替换后被打印出来。由-v命令行选项设置。

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


你如何在设置后禁用回显/详细信息? - Lou

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

bash -x exampleScript.sh

输出结果如下:

enter image description here


你的终端有哪些颜色定义(RGB、数字)? - Peter Mortensen

0

根据Soth的回答。

可以创建一个没有高亮(未指定语言)的Markdown输出。

set -x
exe() { echo "\`\$\$  ${@/eval/} \`" ; "$@" ; }

脚本

set -x
exe() { echo "\`\$\$  ${@/eval/} \`" ; "$@" ; }
echo
echo -----------------------------------------------------------
echo # Setup
echo Lets take a random keyframe from in.mp4:
echo
exe eval "kfn=20"
echo
echo "kf=\$(ffprobe -v error -select_streams v -show_frames -print_format csv in.mp4 | grep 'frame,video,0,1' | head -$kfn | tail -1 | perl -pe 's|frame,video,0,1,.*?,(.*?),.*|\1|') "
exe eval "kf=$(ffprobe -v error -select_streams v -show_frames -print_format csv in.mp4 | grep 'frame,video,0,1' | head -$kfn | tail -1 | perl -pe 's|frame,video,0,1,.*?,(.*?),.*|\1|') "
echo
echo Lets select keyframe at $kf. Here are the timestamps of the all the frames from in.mp4 around this keyframe.
echo
exe eval "ffprobe -v error -select_streams v -show_frames -print_format csv in.mp4  | perl -pe 's|frame,video,0,(.*?),.*?,(.*?),.*|\2  \1|' | perl -pe 's|(.*?)  1|\1\tKF|' |  perl -pe 's|(.*?)  0|\1|' |grep -A 5 -B 5 --color $kf"
echo
echo Lets compare 2 methods of split: actual losslesscut 3.53.0 and another one
echo

输出


让我们从in.mp4文件中随意选取一个关键帧:

$$ kfn=20

kf=$(ffprobe -v error -select_streams v -show_frames -print_format csv in.mp4 | grep 'frame,video,0,1' | head -20 | tail -1 | perl -pe 's|frame,video,0,1,.?,(.?),.*|\1|')

$$ kf=3.803792

让我们选择位于3.803792的关键帧。以下是in.mp4文件中此关键帧周围所有帧的时间戳。

$$ ffprobe -v error -select_streams v -show_frames -print_format csv in.mp4 | perl -pe 's|frame,video,0,(.*?),.*?,(.*?),.*|\2 \1|' | perl -pe 's|(.*?) 1|\1\tKF|' | perl -pe 's|(.*?) 0|\1|' |grep -A 5 -B 5 --color 3.803792

3.720375
3.737083
3.753750
3.770417
3.787125
**3.803792**   KF
3.820500
3.837167
3.853833
3.870542
3.887208

0

如果你只想记录特定的命令,而不是每一行,set -x 使用起来有些困难。但你可以使用这个函数打印任何命令,并在前面加上 $

function run() {
    echo -n '$'  # Print $ without a newline.
    for arg in "$@"; do
      printf " %q" "$arg"  # Print $arg, properly escaped.
    done
    echo  # Print a newline.
    "$@"
}

使用方法:

run ls /full/path/to/some/dir

使用带有%q格式说明符的printf,可以确保输出以适合复制和粘贴到shell中的方式打印,即使存在空格或其他特殊字符。它似乎使用\ 转义空格而不是添加引号,但输出确实正确地表示了实际运行的内容。

0
我使用以下方法来精确控制我想要看到的内容:
#!/bin/bash

cmd="ls $PWD"
echo $cmd; eval $cmd

cmd="cat $PWD/whatfille.txt"
echo "-> $cmd"; eval $cmd      #this prefixes to command for better reading

扩展变量的关键是使用双引号。单引号不会扩展。
有时我会使用“logger”而不是“echo”发送到/var/log/syslog,因为它们经常在非交互式运行。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接