我有这个命令:
cat somefile >file1 >file2
执行此命令后,我不明白为什么file1
其中没有任何内容。它应该有第一个文件 ( ) 的输出somefile
,但其中什么也没有。
你能向我解释一下为什么它不复制或写入我的输出吗somefile
? (file2
包含我的输出,但file1
不包含任何内容)
答案1
我认为您认为 shell 重定向的工作方式与实际工作方式之间存在差异。
除了 in zsh
(tee
当输出被重定向多次时,它会在所看到的后面实现类似的行为),您不能多次重定向 shell 的输出并期望它被重定向到您指定的所有位置。相反,它只会被重定向到最后一个位置,在您的情况下是file2
. Chaos 的答案对 I/O 重定向的工作原理提供了很好的解释。
你真正想做的是:
$ cat example.txt | tee file1 > file2
tee
是一个从标准输入读取并写入多个文件描述符的程序。其中之一是总是标准输出。因此,我们使用tee
将输出写入file1
,然后将其标准输出重定向到file2
.
另外,根据评论中的建议,这是完成您正在寻找的事情的更好方法:
$ tee file1 < example.txt > file2
这种方法的优点是它可以重定向stdin
而不是尝试通过管道读取。这意味着 shell 现在需要少生成一个进程。它还消除了所谓的“猫的无用使用”。通过放置输出重定向后输入重定向,如果输入文件无法打开,我们可以避免输出文件被破坏。
答案2
你所做的,被称为I/O 重定向。>file
将标准输出 (stdout) 重定向到给定的file
.在你的情况下,你做了两次。 shell 不会多次处理同一输出的重定向。
在这种情况下:
cat somefile >file1 >file2
shell 在cat somefile
执行命令 ( ) 之前处理重定向。这意味着>
文件将截断为零长度,因为您覆盖了文件内容。该文件必须为空,shell 才能执行该命令。这是通过两个>
重定向完成的。
现在,第二个 ( >file2
) 会覆盖第一个 ( >file1
),因为 shell 按出现顺序处理重定向。因此,最后一项是有效使用的一项。因此, 的输出cat somefile
将被重定向file2
并被file1
截断为零长度。
tee
将标准输出重定向到多个进程/文件可以这样完成:
cat somefile | tee file1 file2 file3 fileX
这会将内容打印到标准输出和到作为参数给出的所有文件。
答案3
与zsh
多重选项设置(默认情况下),您可以使用:
cat somefile >file1 >file2
在这种情况下,执行类似于幕后的zsh
操作。tee
甚至:
<somefile >file1 >file2
作为 的默认值$NULLCMD
,当有没有命令的重定向时运行的命令恰好是cat
。
答案4
cat somefile >file1 >file2
如果没有重定向,cat somefile
将从 shell 继承 stdin。通常 shell 的 stdin 没有关闭,它已经指向某个文件。终端也是一个文件。当您在标准输出为终端的交互式 shell 中运行时cat somefile
, 的输出cat
将转到终端。一般来说,shell 的标准输出可能是其他不相关的东西,cat
无论如何都会继承它。
它cat somefile >file1
的作用是使外壳:
- 尝试打开
file1
以进行写入(如果文件尚不存在,则会创建文件)并将其截断为零大小(如果可能);这发生在cat
开始之前; - 设置
file1
为未来的标准输出cat
,因此cat
将使用file1
代替如果没有重定向它会使用什么(您不希望cat somefile >file1
也写入您的终端,对吧?)。
重定向重定向。:) 当前方向是“到终端”,新方向是“到file1
”。采取新方向代替当前方向和新方向成为当前方向。
shell 从左到右解析重定向。如果是cat somefile >file1 >file2
外壳,首先>file1
按上述方式进行处理。然后它处理>file2
以同样的方式:它打开并截断文件,并file2
为将来设置“to”cat
代替当前方向此时已经是“to file1
”。这种方式“to file2
”取代了“to file1
”并成为当前状态,就像file1
刚才“to”取代了“to theterminal”一样。
您可以添加更多>fileN
重定向,每个重定向都将按照所描述的方式进行处理。假设每个人都成功,那么最后一个人就会获胜。在你的情况下>file2
获胜。
处理完所有重定向后,cat
开始。您cat
将file2
其视为标准输出,但它甚至不知道file1
以任何方式参与其中。的截断file1
是一个“副作用”,只是因为>file1
已经被shell解析了,与 无关cat
。
您可以使用此机制来创建/截断一个或多个文件:: >file1 >file2 …
将尝试截断文件。:
是一个无操作,它甚至不使用它的标准输出。当 shell 在启动之前处理重定向时,会发生创建和截断:
。
这就是输出重定向在sh
兼容 shell 中的工作方式。中的 MULTIOS 选项zsh
(参见这个另一个答案)不同:第一次重定向替换旧方向(例如终端),而连续重定向添加方向。它是如何在幕后发生的,我不会详细说明。我只是想指出,您观察到的行为来自于每个都被处理sh
(或在兼容的外壳中)的事实>fileN
独立地, 相继;但在zsh
有一种模式下,它将多个输出重定向更像一个单元。