【问题标题】:Bash set +x without it being printedBash set +x 不打印
【发布时间】:2012-10-23 03:17:28
【问题描述】:

有谁知道我们是否可以在不打印的情况下在 bash 中说 set +x

set -x
command
set +x

痕迹

+ command
+ set +x

但它应该只是打印

+ command

Bash 是版本 4.1.10(4)。这让我困扰了一段时间 - 输出中充斥着无用的 set +x 行,这使得跟踪工具没有应有的用处。

【问题讨论】:

  • 这不能回答您的问题,但是当您运行脚本时为什么不回答:script.sh 2>&1 | grep -v 'set +x'

标签: bash shell


【解决方案1】:

我遇到了同样的问题,我找到了不使用子shell的解决方案:

set -x
command
{ set +x; } 2>/dev/null

【讨论】:

  • 很好的答案,请注意:命令后没有分号,这是行不通的;并且在大括号中使用分号但没有空格,将引发语法错误。
  • 这会将退出状态归零。
  • @GarthKidd 退出状态在每个成功的命令中归零。 set +x 是一个如此成功的命令
  • 如果您需要command 的退出状态,此变体是一种解决方案:{ STATUS=$?; set +x; } 2>/dev/null。然后在您空闲的时候检查$STATUS
  • 另外,这里有一个更进一步的版本:{ set +x; } 2>&-。这会直接关闭 fd 2,而不是让它指向 /dev/null。有些程序在尝试打印到 stderr 时处理得不好,这就是为什么 /dev/null 通常是好的样式;但是 shell 的 set -x 跟踪处理得很好,所以它在这里工作得很好,而且它确实使这个咒语更短了。
【解决方案2】:

您可以使用子shell。退出子 shell 后,x 的设置将丢失:

( set -x ; command )

【讨论】:

  • 嗯,谢谢...好点。实际上我知道“子shell技巧”。我希望它可以比这更容易。它涉及代码的大量更改,使代码更复杂且可读性更低。恕我直言,这比使用 set +x 行更糟糕......
  • 我看不出( set -x \n command \n )set -x \n command \n set +x 差多少。
  • @chepner:你不能设置变量。
  • ...你不能cd:它不会改变父shell中的当前目录。
  • 抱歉,我不确定我认为您的真正反对意见是什么。这是漫长的一周……
【解决方案3】:

最近,当我对此感到恼火时,我想出了一个解决方案:

shopt -s expand_aliases
_xtrace() {
    case $1 in
        on) set -x ;;
        off) set +x ;;
    esac
}
alias xtrace='{ _xtrace $(cat); } 2>/dev/null <<<'

这允许您启用和禁用 xtrace,如下所示,我正在记录如何将参数分配给变量:

xtrace on
ARG1=$1
ARG2=$2
xtrace off

您会得到如下所示的输出:

$ ./script.sh one two
+ ARG1=one
+ ARG2=two

【讨论】:

  • 聪明的把戏(虽然你不需要/dev/stdin 部分)。需要注意的是,在脚本中打开别名扩展可能会产生不必要的副作用。
  • 你是对的。我已经编辑了答案以删除多余的/dev/stdin。我不知道任何特定的副作用,因为非交互式环境不应加载任何定义别名的文件。可能有什么副作用?
  • 这是一个很好的观点 - 我忘记了别名不是继承的,所以风险比我想象的要小得多(假设你的脚本可能正在采购碰巧定义别名的第三方代码,但是我同意这可能不是现实世界的问题)。 +1
  • @AndreasSpindler 您能否详细说明为什么您认为这种技术更有可能泄露更多敏感信息?
  • ...基本上是因为别名和函数是高级构造。 set +x 更难妥协(如果不是不可能的话)。但它仍然是一个很好且直接的解决方案 - 可能是迄今为止最好的解决方案。
【解决方案4】:

基于@user108471简化版的解决方案怎么样:

shopt -s expand_aliases
alias trace_on='set -x'
alias trace_off='{ set +x; } 2>/dev/null'

trace_on
...stuff...
trace_off

【讨论】:

  • 这个怎么样? function () { set_plus_x='{ set +x; } 2&gt;/dev/null' }
【解决方案5】:

这是几个想法的组合,可以包含一段代码并保留退出状态。

#!/bin/bash
shopt -s expand_aliases
alias trace_on='set -x'
alias trace_off='{ PREV_STATUS=$? ; set +x; } 2>/dev/null; (exit $PREV_STATUS)'

trace_on
echo hello
trace_off
echo "status: $?"

trace_on
(exit 56)
trace_off
echo "status: $?"

执行时:

$ ./test.sh 
+ echo hello
hello
status: 0
+ exit 56
status: 56

【讨论】:

  • 不错的努力,但我认为exit 周围的() 没有必要。好的。也许这是偏执狂,但如果一般使用此代码,您就有一个很好的攻击向量:重新定义 trace_ontrace_off 并注入读取已执行命令的代码。如果您单独使用此类“实用程序”,则具有指导意义,但如果代码与其他人一起使用,您必须考虑此类非标准化功能的好处是否大于坏处。就我个人而言,我已经与{ set +x; } 2&gt;/dev/null 解决了,因为这个结构是普遍理解的,也不会改变退出状态。
  • 谢谢。我已经与我的同事分享了这种方法,他们喜欢它如何使输出和代码的噪音降低。关于exit 56周围的();这只是为了证明可以在 bash 脚本中访问子进程的退出状态;如果没有括号,它就不会成为子进程,并且脚本会在那个时候以 status=65 退出。
【解决方案6】:

这是另一个。通常您只想跟踪脚本中的特定命令。那为什么不写一个这样的函数呢?

> call() { set -x; "$@"; { set +x; } 2>/dev/null; }
> call uname -a
+ uname -a
CYGWIN_NT-6.1-WOW W530 3.1.7(0.340/5/3) 2020-08-22 19:03 i686 Cygwin
> call make -j8 *.mak
+ make -j8 some_name.mak

我们可以通过返回(并跟踪)被调用命令的退出代码来进一步改进这一点:

> call() { local rc; set -x; "$@"; rc=$?; { set +x; } 2>/dev/null; return $rc; }
> call true && echo yes
+ true
+ rc=0
yes
> call false && echo yes
+ false
+ rc=1

这是一个真实的例子,它调用 Rhapsody CLI 程序从带有命令行选项的 .rcl 文件生成源代码:

die() {
    local c=${1:--1} m=${2:-'Died'}
    echo "$m at ${BASH_SOURCE[1]}:${FUNCNAME[1]} line ${BASH_LINENO[0]} (exit $c)" >&2
    exit $c
}
call() { local rc; set -x; "$@"; rc=$?; { set +x; } 2>/dev/null; return $rc; }

call "$opt_rhapsodycl" -f $rclfile || die $? 'Rhapsody license server not reachable'

例如,在失败的情况下打印如下内容:

 + path/to/RhapsodyCL.exe -f configuration.rcl
 + rc=127
 Rhapsody license server not reachable at ./Build:main line 167 (exit 127)

或者在成功的情况下脚本继续。使用这两个函数(calldie),我们可以编写非常紧凑和可读的单行代码,同时也可以产生很好的跟踪输出。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-01-25
    • 2012-05-13
    • 2015-06-11
    • 1970-01-01
    • 1970-01-01
    • 2020-10-15
    • 1970-01-01
    • 2022-01-04
    相关资源
    最近更新 更多