在重定向上附加字符串

在重定向上附加字符串

将所有输出重定向到 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

相关内容