将进程的输出重定向到文件和流?


概述

我们将了解一些将进程输出重定向到文件和标准流(如 STDOut 和 STDERR)的同时的方法。

tee 命令

Tee 是我们最常用的 Linux 命令行工具之一,可用于重定向进程的输出。它也称为“teeing”或“piping”。tee 命令接受两个参数 - 您希望将重定向的输出保存到的文件名,以及将用于写入原始输入的另一个文件名。

重定向stdout

开始了!我们将看一个简单的例子,将 ls(列表)命令的输出重定向到 stdout 和一个名为 out.log 的临时文件。

$ ls -C | tee /tmp/out.log
bin   dev  home mnt  proc  run  srv  tmp  var
boot  etc  lib64	 opt  root  sbin  sys  usr

我们可以确认文件的内容与执行命令生成的输出匹配。

$ cat /tmp/out.log
bin   dev  home mnt  proc  run srv  tmp  var
boot  etc  lib64	 opt  root  sbin  sys  usr

另一个需要记住的重要一点是,tee 命令的默认行为是替换文件的内容。但是,如果需要,我们可以使用 -a(追加)命令在文件的现有内容之后添加新内容。

stdoutstderr 重定向到同一个文件

在内部,tee 命令充当传入流的 T 形分流器,以便可以将数据同时定向到输出流和一个或多个文件。我们可以利用此知识将进程的 stderrs 重定向到 stdouts 和文件。

$ (ls -C; cmd_with_err) 2>&1 | tee /tmp/out.log
bin   dev  home mnt  proc  run srv  tmp  var
boot  etc  lib64	 opt  root  sbin  sys  usr
bash: cmd_with_err: command not found

我们可以看到 cmd_with_error 是一个未知命令,这意味着它会产生错误消息。我们将 stderror fd(fd=2)重定向到 stdin fd(fd=1),以便 tee 可以同时从两个文件读取。

或者,我们也可以使用 |& 作为 2>&1| 的简写来获得相同的结果 -

$ (ls -C; cmd_with_err) |& tee /tmp/out.log
bin   dev  home mnt  proc  run srv  tmp  var
boot  etc  lib64	 opt  root  sbin  sys  usr
bash: cmd_with_err: command not found

现在,让我们验证 /tmp/out.log 文件的内容 -

$ cat /tmp/out.log
bin   dev  home mnt  proc  run srv  tmp  var
boot  etc  lib64	 opt  root  sbin  sys  usr
bash: cmd_with_err: command not found

stdoutstderr 重定向到不同的文件

有时我们可能希望将进程的输出重定向到两个不同的文件。我们可以使用进程替换来调用 tee 命令。在详细介绍 tee() 函数之前,以下是一个启用 tee() 函数监听特定 fd(文件描述符)并写回原始 fd 流和文件的模板代码片段示例。

fd> >(tee file_name fd>&fd)

我们必须指出,fd 只是一个文件描述符的示例;实际值对于 stdout 为 1,对于 stderror 为 2,对于 stdin 为 0。

现在,让我们利用我们对流的理解,将命令的 stdout 流重定向到 /tmp/out,将 stderr 流重定向到 /tmp/err。

$ ((ls -C; cmd_with_err) 1> >(tee /tmp/out.log)) 2> >(tee /tmp/err.log 2>&2)
bin   dev  home mnt  proc  run srv  tmp  var
boot  etc  lib64	 opt  root  sbin  sys  usr
bash: cmd_with_err: command not found

我们可以确认 /tmp/outfile.log 具有正确的输出,但 /tmp/errorfile.log 具有错误的错误消息。

$ cat /tmp/out.log
bin   dev  home mnt  proc  run srv  tmp  var
boot  etc  lib64	 opt  root  sbin  sys  usr
$ cat /tmp/err.log
bash: cmd_with_err: command not found

重定向延迟

调用 tee (tee) 命令将进程的输出定向到文件和 STDOUT 可能会导致延迟。我们将研究一个我们可能希望避免的特定情况,并学习在发生这种情况时如何减轻它。

场景

开始了!我们将编写一个 Python 程序,每秒打印一次当前时间。

$ cat time.py
#!/usr/bin/python
from datetime import datetime
import time
import sys
from sys import stdout
while True:
   sys.stdout.write(datetime.today().strftime("%H:%M:%S %p
"
))     time.sleep(1)

如果我们运行此脚本,我们将看到脚本打印出的每个时间戳之间有 1 秒的延迟。

$ ./time.py
   9:29:48 PM
   9:29:49 PM
   9:29:50 PM
   9:29:51 PM
   9:29:52 PM
   9:29:53 PM

延迟重定向

现在,让我们使用“te”命令将此脚本的输出重定向到 stdout 和“time.out”日志。

$ ./time.py | tee time.out

我们将看到,stdout 在很长一段时间内没有任何输出打印,然后会一次性打印出一大块 stdout。

Python 的内部缓冲区大小设置为 1MB,这意味着当写入 stdout 时,输出可能会延迟长达 1 秒才能实际出现在屏幕上。缓冲策略会导致写入 stdout 的内容通过 4096 字节(或更大)的缓冲区传递,从而减少输出文本到终端所需的 I/O 次数。

对于交互式应用程序,必须避免从一个页面重定向到另一个页面的任何延迟。为了解决重定向延迟问题,我们需要寻找减轻它的方法。

缓解措施

解决此问题的一种方法是确保定期将程序的输出刷新到控制台。

$ cat time.py
#!/usr/bin/python
from datetime import datetime
import time
import sys
from sys import stdout
while True:
   sys.stdout.write(datetime.today().strftime("%H:%M:%S %p
"
)) sys.stdout.flush() time.sleep(1) Let's check if the delay has been removed: $ ./time.py | tee time.out 21:46:12 PM 21:46:13 PM

我们可以直接控制应用程序代码,因此我们可以更改它。

但是,在许多情况下,程序可能是编译后的二进制文件,我们可能无法访问其代码进行修改。如果我们想避免与写入 stdout 相关的延迟,我们可以使用 Linux 中的“unbuffer”程序来解决此问题。

让我们从我们的代码中删除 sys.stdou​t.flus​h() 方法调用,并使用 unbuffer ​命令再次运行重定向。

$ unbuffer ./time.py | tee time.out
21:52:22 PM
21:52:23 PM

更改代码后,stdout 写入操作没有出现意外延迟。

结论

我们查看了将程序的输出同时重定向到 stdin 和 stdouts 的几个示例。我们还学习了一些解决缓冲请求导致的重定向延迟问题的技巧。

更新于: 2022-12-26

895 次浏览

开启你的 职业生涯

通过完成课程获得认证

开始学习
广告