我在 ksh 中有一个脚本;根据是否存在来自命令行的重定向,我通过exec 1>file
.如果调用脚本的命令已重定向其输出,如何从脚本本身内部进行测试?
我尝试在脚本的 PID 上使用$@
、$*
、$0
、 甚至 a ps
(希望有一个 shebang),但重定向从未出现。
在本例中,该脚本在 AIX 上运行。
答案1
在 AIX 上,stdout 文件描述符可在 处获得/proc/$$/fd/1
,因此您可以测试它是否是常规文件:
if [ -f "/proc/$$/fd/1" ]
then
echo stdout has already been redirected
else
echo redirecting stdout
exec 1>file
echo some output
fi
/bin/sh 硬链接到 /bin/ksh,因此您在任一 shell 中都会获得相同的行为。
如果需要,您可以单独测试 stdout 是否已重定向到 /dev/null:
if [ "/proc/$$/fd/1" -ef /dev/null ]; then : ...; fi
答案2
一般来说,你不能。重定向不会作为运行命令的参数出现。即使他们这样做了,您也无法在所有情况下都知道脚本输出的去向。考虑这两个:
bash -c 'somecmd > /dev/null; othercmd'
和
bash -c 'somecmd; othercmd' > /dev/null
在第一种情况下, 的输出somecmd
被重定向到/dev/null
,但在第二种情况下,整个 shell 的输出都被重定向,包括somecmd
和othercmd
。在第二种情况下查看命令行somecmd
并不能说明输出是如何重定向的。
也就是说,Bash 的DEBUG
陷阱似乎可以用于此目的。
$ trap 'export CMDLINE=$BASH_COMMAND' DEBUG
$ env 2>/dev/null |grep CMD
CMDLINE=env 2> /dev/null
陷阱导出要运行的命令CMDLINE
,我们可以看到该命令已导出,因为它显示在 的输出中env
。请注意,未显示完整的管道,仅显示一个命令。
也就是说,在大多数情况下,有比尝试事后猜测用户的重定向更好的方法来处理问题。许多命令会检查输出是否发送到终端,并据此更改其行为。
要检查 stdout 是否是终端,您可以使用[ -t 1 ]
:
$ if [ -t 1 ]; then echo terminal; else echo not terminal; fi |cat
not terminal
这最常用于禁用某些交互功能或无关输出,以防输出未到达终端,因此根据假设,不会到达用户。
如果仅仅测试文件描述符是否指向终端还不够,那么最简单的方法可能是安排向程序传递一个附加参数来告诉它要在什么模式下运行。也就是说,不关心重定向,而是让如果以 启动,则程序执行一件事someprog --mode=cron
,如果以 启动,则执行另一件事;someprog --mode=batch
如果在不带参数的情况下启动,则以交互方式运行--mode
。 (将交互或命令行模式设置为默认模式,以便用户--mode=commandline
每次手动运行时都不需要手动键入。)
答案3
在我的测试中,在Linux(Debian 10.12)上使用Bash 5(GNU bash,版本5.0.3(1)-release (x86_64-pc-linux-gnu)
),可以使用[ -p
测试。
- 考虑一个脚本
test.sh
#!/bin/bash ls -lah /proc/$$/fd/1 if [ -p "/proc/$$/fd/1" ]; then echo "pipe"; else echo "nopipe"; fi exec 1> >( cat ) 2>&1 ls -lah /proc/$$/fd/1 if [ -p "/proc/$$/fd/1" ]; then echo "pipe"; else echo "nopipe"; fi
- 在不带重定向和带重定向的情况下执行脚本:
$ ./test.sh lrwx------ 1 [...] /proc/123/fd/1 -> /dev/pts/2 nopipe l-wx------ 1 [...] /proc/123/fd/1 -> pipe:[12345] pipe $ ./test.sh | cat l-wx------ 1 [...] /proc/124/fd/1 -> pipe:[12346] pipe l-wx------ 1 [...] /proc/124/fd/1 -> pipe:[12347] pipe