LeP*_*bre 5 bash io-redirection stdout stderr
我是 Linux 新手,并试图了解重定向的工作原理。
我一直在测试用于重定向stdout
和stderr
到同一个文件的各种语法,这些语法并不都产生相同的结果。
例如,如果我尝试列出 2 个不存在的文件 ( file1
and file2
) 和 2 个不存在的文件( foo
and fz
):
语法 #1(无重定向):
$ ls file1 foo fz file2
Run Code Online (Sandbox Code Playgroud)
这是我在终端中得到的输出:
ls: cannot access file1: No such file or directory
ls: cannot access file2: No such file or directory
foo fz
Run Code Online (Sandbox Code Playgroud)
语法#2:
现在,通过重定向:
$ ls file1 foo fz file2 > redirect 2>&1
Run Code Online (Sandbox Code Playgroud)
该redirect
文件包含与语法 #1 的结果相同:
ls: cannot access file1: No such file or directory
ls: cannot access file2: No such file or directory
foo
fz
Run Code Online (Sandbox Code Playgroud)
因此,对于上述两种语法,shell 似乎stderr
先打印,然后打印stdout
.
语法#3:
现在,如果我尝试使用以下任一语法:
$ ls file1 foo fz file2 > redirect 2> redirect
Run Code Online (Sandbox Code Playgroud)
或者
$ ls file1 foo fz file2 2> redirect > redirect
Run Code Online (Sandbox Code Playgroud)
然后该redirect
文件将包含以下内容:
foo
fz
nnot access file1: No such file or directory
ls: cannot access file2: No such file or directory
Run Code Online (Sandbox Code Playgroud)
这里看起来像是stdout
之前打印的stderr
,但是我们看到 的开头stderr
被“裁剪”了与stdout
.
的stdout
是6个字符长(foo fz
,包括回车),所以第一6个字节的stderr
(ls: ca
)已被覆盖通过stdout
。所以它实际上似乎stderr
是先打印的,stdout
然后打印出来stderr
而不是附加到它上面。
但是,它会更有意义,我如果stderr
被完全删除,取而代之的stdout
,而不仅仅是部分overwitten。
语法#4:
我发现纠正语法 #3 的唯一方法是将 append 运算符添加到stdout
:
$ ls file1 foo fz file2 >> redirect 2> redirect
Run Code Online (Sandbox Code Playgroud)
或者
$ ls file1 foo fz file2 2> redirect >> redirect
Run Code Online (Sandbox Code Playgroud)
产生与语法#2 相同的结果:
ls: cannot access file1: No such file or directory
ls: cannot access file2: No such file or directory
foo
fz
Run Code Online (Sandbox Code Playgroud)
这里的这篇文章解释了语法 #3 是错误的(大概,语法 #4 也是如此)。但是为了论证:为什么语法#3 是错误的?与语法#2 相比,它究竟告诉(或不告诉)shell 做什么?
另外,有没有原因为什么输出总是显示stderr
在之前stdout
?
谢谢!
这就像运行两个进程同时写入同一个文件......坏主意。您最终会得到两个不同的打开文件句柄,并且您的数据可能会出现乱码(就像上面 #3 中那样)。使用语法#2 是正确的;它生成一个文件句柄并将 stderr 和 stdout 指向同一位置。
至于 stderr 总是先打印,对此没有任何规定。我怀疑ls
是因为ls
需要检查目录中的每个条目,然后才能实际声明特定文件不存在。因此,它不是在目录表上进行 N 次传递,而是进行一次传递,检查给定的所有命令行参数,报告错误并打印它找到的文件。其他命令可能会在 stdout 之后打印到 stderr,甚至在它们之间交替。
添加到通配符的答案中,stdout
通常是缓冲的(存储在内存中以便稍后写出),而stderr
从不(您希望显示错误消息,即使程序在写出任何内容之前崩溃)。所以stderr
通常会更早出现。
不是这里的开发人员或内核专家。所以我无法评论任何代码,但在语法 #3 中,您使用两个不同的管道通过管道连接stdout
到stderr
同一个文件。因此,您将两个输出发送到的公共文件可能存在来自两个不同管道的资源争用问题。考虑到操作系统的非实时性质,两个管道可能同时将信息输入到同一个文件中。这可能可以解释缺失的字符。
在语法 #2 中,您将其重定向stderr
到同一个管道,stdout
此时,该文件有一个管道进入,并且 shell 根据竞争条件管理该管道。
这是我有根据的猜测,也只是猜测。没有什么可以证实这一点。