当您站着鳄鱼皮时,很容易忘记目标是排干沼泽。
-俗语
问题是关于echo
,但到目前为止,大多数答案都集中在如何潜入set +x
命令上。有一个更简单,更直接的解决方案:
{ echo "Message"; } 2> /dev/null
(我承认,{ …; } 2> /dev/null
如果我在先前的答案中没有看到它,可能没有想到。)
这有点麻烦,但是,如果您有一组连续的echo
命令,则不需要分别对每个命令执行此操作:
{
echo "The quick brown fox"
echo "jumps over the lazy dog."
} 2> /dev/null
注意,换行时不需要分号。
您可以通过使用kenorb的想法来减轻打字负担,该想法
是/dev/null
在非标准文件描述符(例如3)上永久打开,然后一直说2>&3
而不是说2> /dev/null
。
撰写本文时,前四个答案要求您每次执行时都要做一些特别的事情(在大多数情况下是麻烦的)
echo
。如果您确实希望所有 echo
命令都抑制执行跟踪(为什么不这样做?),则可以全局执行此操作而无需花费很多代码。首先,我注意到没有跟踪别名:
$ myfunc()
> {
> date
> }
$ alias myalias="date"
$ set -x
$ date
+ date
Mon, Oct 31, 2016 0:00:00 AM # Happy Halloween!
$ myfunc
+ myfunc # Note that function call is traced.
+ date
Mon, Oct 31, 2016 0:00:01 AM
$ myalias
+ date # Note that it doesn’t say + myalias
Mon, Oct 31, 2016 0:00:02 AM
(请注意#!/bin/sh
,即使shebang为/bin/sh
bash的链接,以下脚本片段也可以使用,即使shebang为bash的链接也可以。但是,如果shebang为#!/bin/bash
,则需要添加shopt -s expand_aliases
命令以获取别名以在脚本中工作。)
因此,对于我的第一个技巧:
alias echo='{ set +x; } 2> /dev/null; builtin echo'
现在,当我们说时echo "Message"
,我们正在调用别名,该别名不会被跟踪。别名关闭跟踪选项,同时抑制set
命令中的跟踪消息(使用user5071535的答案中首先介绍的技术),然后执行实际echo
命令。这使我们获得了类似于user5071535的答案的效果,而无需在每个 echo
命令中都编辑代码。但是,这将使跟踪模式关闭。我们不能将a set -x
放入别名中(或至少不容易),因为别名仅允许将字符串替换为单词;在参数(例如)之后不能将别名字符串的任何部分注入到命令中
"Message"
。因此,例如,如果脚本包含
date
echo "The quick brown fox"
echo "jumps over the lazy dog."
date
输出将是
+ date
Mon, Oct 31, 2016 0:00:03 AM
The quick brown fox
jumps over the lazy dog.
Mon, Oct 31, 2016 0:00:04 AM # Note that it doesn’t say + date
因此,在显示消息后,您仍然需要重新打开trace选项,但是在每个连续命令块之后仅需一次echo
:
date
echo "The quick brown fox"
echo "jumps over the lazy dog."
set -x
date
如果能在set -x
之后制造自动机,那将是一件很不错的事,而我们可以做些echo
棘手的事情。但是在我提出这一点之前,请考虑一下。OP从使用#!/bin/sh -ex
shebang的脚本开始。隐含地,用户可以x
从shebang中删除,并使脚本正常运行,而无需执行跟踪。如果我们可以开发一种保留该属性的解决方案,那就太好了。此处的前几个答案使该属性失败,因为它们echo
无条件地在after 语句中跟踪“返回” ,而不管其是否已打开。
此答案很明显无法识别该问题,因为它代替了 echo
输出带跟踪输出;因此,如果关闭了跟踪,则所有消息都将消失。现在,我将提出一种解决方案,该解决方案在有条件的echo
语句之后(仅当该语句已经打开时)才重新打开。将其降级为可无条件启用“追溯”的解决方案是微不足道的,应留作练习。
alias echo='{ save_flags="$-"; set +x;} 2> /dev/null; echo_and_restore'
echo_and_restore() {
builtin echo "$*"
case "$save_flags" in
(*x*) set -x
esac
}
$-
是选项列表;与设置的所有选项相对应的字母的串联。例如,如果设置了e
和x
选项,$-
则将是包含e
和的字母混杂在一起x
。我的新别名(上面)保存了$-
关闭跟踪之前的值。然后,在关闭跟踪的情况下,它将控制权交给外壳函数。该函数执行实际操作echo
,然后检查x
调用别名时是否打开了该选项。如果该选项打开,则该功能将其重新打开;否则,该功能将重新打开。如果已关闭,则该功能将其关闭。
您可以shopt
在脚本的开头插入上面的七行(如果包含,则插入八行),其余的保持独立。
这可以让你
- 使用以下任何shebang行:
#!/ bin / sh -ex
#!/ bin / sh -e
#!/ bin / sh -x
或只是普通#!/ bin / sh
它应该可以按预期工作。
- 具有类似的代码
(shebang)
命令1
命令2
命令3
设置-x
命令4
命令5
命令6
设置+ x
命令7
命令8
命令9
和
- 将跟踪命令4、5和6,除非其中一个命令是an
echo
,在这种情况下将执行但不跟踪。(但即使命令5是echo
,命令6仍将被跟踪。)
- 不会跟踪命令7、8和9。即使命令8是
echo
,命令9仍然不会被跟踪。
- 根据shebang是否包含,将跟踪命令1、2和3(如4、5和6)(如7、8和9)
x
。
PS我发现,在我的系统上,我可以builtin
在中间答案中忽略该关键字(该关键字只是的别名echo
)。这不足为奇。bash(1)表示,在别名扩展期间,…
…与扩展别名相同的单词不会再次扩展。这意味着一个可能的别名ls
来ls -F
,例如,和bash不会尝试递归扩展替换文本。
毫不奇怪,echo_and_restore
如果builtin
省略关键字1,则最后一个答案(带有的答案)将失败。但是,奇怪的是,如果我删除builtin
并切换顺序,它会起作用:
echo_and_restore() {
echo "$*"
case "$save_flags" in
(*x*) set -x
esac
}
alias echo='{ save_flags="$-"; set +x;} 2> /dev/null; echo_and_restore'
__________
1 似乎引起了不确定的行为。我见过
- 一个无限循环(可能是由于无限制的递归),
- 一个
/dev/null: Bad address
错误消息,并且
- 一个核心转储。
echo +x; echo "$*"; echo -x
一种别名中的等效方法,我想看看。