将所有输出重定向到 stderr 时:
stat file >&2
...如果命令抛出错误(例如文件不存在),如何在前面添加一些任意文本,例如:
<<MY ARBITRARY TEXT>>: stat: cannot stat 'file': No such file or directory
所需的逻辑是:
let res = stat file
if error then output "<<MY ARBITRARY TEXT>>"+error to stderr
else output res to stderr
更多上下文:所有命令输出都被重定向到 stderr,因为 stdout 和 stderr 流总是以正确的顺序返回,即,如果发送多个命令并且其中一个抛出错误,则下一个命令的 stdout 可能会在原始命令的 stderr 之前返回命令。为了避免这种情况,我将所有命令合并到 stderr 上,但理想情况下希望能够轻松识别错误。
答案1
长话短说:动态解析 stderr 可能很复杂,因此通过将 stderr 存储到变量中并稍后操作变量内容来简化它。
您可能需要考虑做的是捕获stderr
变量,然后检查它是否不为空。考虑下面的例子:
$ foo=$(stat /etc/passwd 2>&1 > /dev/tty )
File: /etc/passwd
Size: 1710 Blocks: 8 IO Block: 4096 regular file
Device: fd00h/64768d Inode: 537594 Links: 1
Access: (0644/-rw-r--r--) Uid: ( 0/ root) Gid: ( 0/ root)
Access: 2019-03-08 15:28:10.481688274 -0700
Modify: 2019-03-08 15:28:10.333669255 -0700
Change: 2019-03-08 15:28:10.341670283 -0700
Birth: -
$ test "x$foo" = "x" && echo "empty"
empty
命令替换$(...)
运行一个子 shell 并stdout
仅捕获,因此我们希望将文件描述符 1 复制到 2 上,2>&1
以便现在可以通过命令替换捕获 stderr,但是我们仍然希望在屏幕上看到正常输出,因此之后我们指向stdout
控制终端/dev/tty
。
现在,如果我们实际上将 stderr 捕获到变量中会怎样?
$ foo=$(stat nonexistent.txt 2>&1 > /dev/tty )
$ test "x$foo" = "x" && echo "empty" || echo "not empty"
not empty
$ printf "<Arbitrary Text>%s\n" "$foo"
<Arbitrary Text>stat: cannot stat 'nonexistent.txt': No such file or directory
正如您所看到的,因为stderr
已被捕获,我们可以根据需要在其周围包裹任意文本。使用变量的主要原因是标准 shell 方法(例如管道和命令替换)对stdout
文件描述符 1 进行操作,因此我们无法进行太多stderr
动态解析。当然,我们可以使用命名管道并将其重定向为stat foobar.txt 2> /tmp/named_pipe.fifo
,但这样做的问题是阻塞 - 发送到管道的信息会被缓冲,直到被另一个进程消耗,因此您的 shell 脚本将被卡住。当然,这可以通过启动后台进程来处理,但恕我直言,它比必要的更加复杂,并且使用像 Python 这样的编程语言来执行多个进程要好得多,您实际上可以直接访问多处理调用。
如果您希望 stdout 和 stderr 都在单独的变量中进行处理,有一个很好的例子在 Stack Overflow 上,这可以通过命名管道以及一些特定于 shell 的方法/技巧来实现。
旁注:test
和[
是相同的命令,因此您也可以[ "x$foo" = "x" ]
在更详细的 if-else 语句中编写和使用它
答案2
您可以非常简单地实现您想做的事情。
1)如果您需要恢复文件中的输出foobar
:
$ (echo "Arbitrary text" && stat /etc/passwd ) 2>&1 | tee foobar
2)更简单地说,如果您关心的只是输出到stdout
:
$ (echo "Arbitrary text" && stat /etc/passwd ) 2>&1