我编写了一个非常大的 bash 脚本,用于安装软件包和配置 LAMP 堆栈等。对于我通常编写的任何脚本,我喜欢使用 echo 命令来整理它,以便运行脚本的用户知道屏幕上发生了什么,并使用以下方法抑制所有已执行命令的输出:> /dev/null 2>&1。目前我的脚本采用以下形式:(注意:使用/dev/null 2>&1是我想避免使用的)
echo -n "Starting foo... "
command 1 > /dev/null 2>&1
command 2 > /dev/null 2>&1
echo "Done"
现在我可以继续使用所有基于 Unix 的脚本执行此操作,但考虑到我拥有的代码行数,这肯定是一项繁琐的工作。幸运的是,我手头有大量的命令注释,因此可以轻松将它们转换为 echo 命令。但是,我仍然只想传递 echo 命令并隐藏其他每个命令的输出,而无需> /dev/null 2>&1。但是,如果确实发生错误,我仍然希望显示这些。
是否存在一个全局参数可以禁用除 echo 等某些命令之外的标准输出?
更新2
我在 stackoverflow 上找到了一个与我类似的帖子这里
exec 3>&1 &>/dev/null
some_command
another_command
command_you_want_to_see >&3
command3
3>&1
将任何以 为后缀的命令重定向>&3
到新的文件描述符,该文件描述符将重定向到外部&1
(STDOUT)。关于&>/dev/null
这一点,我假设这会抑制所有输出到/dev/null
?
在我的示例中,此脚本如下:
#!/bin/bash
# Hide all output by default
exec 3>&1 &>/dev/null
echo -n "outputting text to file... " >&3
echo "hello world" > ~/"hello_world_output.txt"
echo "I am hidden from output"
echo "Done" >&3
产生以下输出:
user@ubuntu:~$ ./test.sh
outputting text to file... Done
user@ubuntu:~$
更新 3
您能否将以下代码片段放入示例脚本中,以便我可以通过一些示例命令将其作为一个整体来查看?第一个例子说它可以在脚本的开头定义。我是否应该将脚本的其余部分放在闭合括号下?
echo() (
if { true >&3; } 2>/dev/null; then
exec >&3
fi
command echo "$@"
)
我是否再次将脚本的其余部分放在此代码片段下?
exec 3>&1 >/dev/null
echo() (
>&3 command echo "$@"
)
答案1
总结
定义函数
report() { echo "$@" >&2; }
并像 一样使用它echo
,即report -n "Starting foo... "
或report "Done"
。运行脚本并将 stdout 重定向到/dev/null
:
/path/to/the_script >/dev/null
或者通过在脚本开头运行来让脚本将其 stdout 重定向到/dev/null
自身exec >/dev/null
。这样,您在调用脚本时就无需键入>/dev/null
;但是如果您不想重定向,则需要修改脚本(删除exec >/dev/null
)。
不抑制错误是通过保留 stderr 来实现的。该函数打印到 stderr,因为像 这样的消息Starting foo...
可以被视为诊断,stderr 并不是它们最糟糕的地方。
完整答案
是否存在一个全局参数可以禁用除某些命令(例如)之外的标准输出
echo
?
我认为没有。但是您可以制作自定义函数echo
并通过这种方式实现您想要的效果。在脚本开头定义此函数:
echo() (
if { true >&3; } 2>/dev/null; then
exec >&3
fi
command echo "$@"
)
这将导致echo
脚本中的每个脚本都运行该函数,而不是常规的echo
。该函数检查文件描述符 3 是否可用。如果可用,则该函数会将其自己的 stdout 重定向到它。由于该函数定义为在子 shell 中运行,因此这不会影响主脚本;但这command echo
最终会影响从函数运行。
因此,该函数会echo
优先使用文件描述符 3(只要可用)。现在,如果您像这样运行脚本:
/path/to/the_script 3>&1 >/dev/null
然后echo
s 中的命令将使用没有重定向时使用的 stdin the_script
(可能是终端),而其他命令将打印到/dev/null
。注意,你仍然可以运行
/path/to/the_script
并且它会正常运行。
函数的定义可以放在一个单独的文件中。每个需要该函数的脚本都可以 source 该文件。
如果你不喜欢打字/path/to/the_script 3>&1 >/dev/null
,而且你希望 sole/path/to/the_script
以新的方式运行,那么让它the_script
为自己做适当的重定向:
exec 3>&1 >/dev/null
因此,要获取的整个代码片段可以是这样的:
exec 3>&1 >/dev/null
echo() (
>&3 command echo "$@"
)
请注意,没有必要,if
因为我们刚刚打开了文件描述符 3。
另一方面,您可能不想更改脚本。如果解释器是 Bash,那么您可以通过环境将函数传递给它。这意味着您可以这样做:
# in Bash
(
echo() (
>&3 command echo "$@"
)
export -f echo
/path/to/the_script 3>&1 >/dev/null
# where the_script is the original (unmodified) script
)
我将整个代码片段放在一个子 shell 中,因此如果您在交互式 Bash 中运行它,该函数将不会保留,也不会影响后面的命令。
这让我们找到了一个可以静音其他用户的 Bash 脚本Bash 脚本,但echo
其中不包括:
#!/bin/bash
echo() (
>&3 command echo "$@"
)
export -f echo
exec 3>&1 >/dev/null "$@"
将其保存为silence
中的某个位置PATH
,使其可执行。然后像这样使用它:
silence /path/to/the_script
笔记:
通过保留 stderr 不变,可以不抑制错误。
如果
the_script
运行其他 Bash 脚本,则在环境中传递该函数也会影响它们。请注意,您可能不知道您使用的某些工具是 Bash 脚本。如果您在the_script
不导出函数的情况下(直接或通过源)定义该函数,则其他脚本不会受到影响。函数将重定向(因此休息) 每种情况都旨在
echo
打印到脚本的标准输出以外的其他地方。示例:echo Message >>log echo 1 2 3 | pipeline
最后一点是一个严重的缺点。因此,请考虑将echo
其保留。重建脚本,不要echo
直接用于报告进度。而不是echo "Done"
运行report "Done"
,其中report
是类似于上面介绍的函数之一的函数echo
。这样,该函数就不会影响随机echo
数。如果我是你,我甚至不会对文件描述符 3 使用这个技巧。我会report
简单地打印到 stderr。像这样的消息Starting foo...
可以被视为诊断,stderr 并不是它们最糟糕的地方。我会使用的功能:
report() { echo "$@" >&2; }