当我尝试对文件进行排序并保存已排序的输出时,就像这样
sort file1 > file1;
file1的内容完全被删除,而当我试图用'tee'这样的命令做同样的事情时
sort file1 | tee file1;
它工作正常 [编辑:“工作正常”仅适用于幸运时间的小文件,会导致大数据丢失或无用的进程调度] ,即它会覆盖排序的输出file1本身,也在标准输出上显示。
有人可以解释为什么第一个案例不起作用吗?
答案 0 :(得分:18)
正如其他人所解释的那样,问题是I / O重定向是在sort
命令执行之前完成的,所以在sort
有机会读取之前文件会被截断。如果你想一点,原因很明显 - shell处理I / O重定向,并且必须在运行命令之前执行此操作。
sort
命令'always'(因为至少版本7 UNIX)支持-o
选项,以便安全地输出到其中一个输入文件:
sort -o file1 file1 file2 file3
tee
的技巧取决于时间和运气(可能是一个小数据文件)。如果您有一个兆字节或更大的文件,我预计它会被tee
命令破坏,至少部分是破坏。也就是说,如果文件足够大,tee
命令将打开文件进行输出,并在sort
完成读取之前将其截断。
答案 1 :(得分:14)
它不起作用,因为'>'重定向意味着截断,并且为了避免在重定向到文件之前将sort
的整个输出保留在内存中,bash在运行sort
之前截断并重定向输出。因此,在file1
有机会阅读之前,sort
文件的内容将被截断。
答案 2 :(得分:4)
依赖这些命令中的任何一个以你期望的方式工作是不明智的。
修改文件的方法是将修改后的版本写入新文件,然后将新文件重命名为原始名称:
sort file1 > file1.tmp && mv file1.tmp file1
这样可以避免在文件被部分修改后读取文件的问题,这可能会导致结果混乱。它还可以优雅地处理错误;如果文件长度为N个字节,并且文件系统上只有N / 2个字节的可用空间,则可以检测创建临时文件的失败而不进行重命名。
或者您可以重命名原始文件,然后读取它并写入具有相同名称的新文件:
mv file1 file1.bak && sort file1.bak > file1
某些命令具有修改文件的选项(例如,perl
和sed
都有-i
个选项(请注意,sed的-i
选项的语法可能会有所不同但是这些选项可以通过创建临时文件来实现;它只是在内部完成。
答案 3 :(得分:1)
Bash在读取管道时打开一个新的空文件,然后调用sort。
在第二种情况下,在排序已经读取内容后,tee打开文件。
答案 4 :(得分:1)
重定向具有更高的优先级。所以在第一种情况下,> file1首先执行并清空文件。
答案 5 :(得分:1)
第一个命令不起作用(sort file1 > file1
),因为在使用重定向操作符(>
或>>
)时,shell会在sort
命令之前创建/截断文件甚至被调用,因为它具有更高的优先级。
第二个命令有效(sort file1 | tee file1
),因为sort
首先从文件中读取行,然后将已排序的数据写入标准输出。
因此,当使用任何其他类似命令时,您应该避免在读取和写入同一文件时使用重定向操作符,但您应该使用相关的就地编辑器(例如ex
,ed
,sed
),例如:
ex '+%!sort' -cwq file1
或使用其他工具,例如sponge
。
幸运的是sort
有-o
参数将结果写入文件(由@Jonathan建议),因此解决方案很简单:sort -o file1 file1
。< / p>
答案 6 :(得分:-2)
您可以使用此方法
sort file1 -o file1
这将排序并存储回原始文件。此外,您可以使用此命令删除重复的行:
sort -u file1 -o file1