111

我有一个文件 temp.txt,我想用sortbash 中的命令对其进行排序。

我希望排序结果替换原始文件。

例如,这不起作用(我得到一个空文件):

sortx temp.txt > temp.txt

这可以在一行中完成而无需复制到临时文件吗?


编辑:该-o选项对于sort. 我sort以我的问题为例。我在使用其他命令时遇到了同样的问题:

uniq temp.txt > temp.txt.

有更好的通用解决方案吗?

4

14 回答 14

170
sort temp.txt -o temp.txt
于 2008-09-28T18:45:41.410 回答
29

Asort需要先查看所有输入,然后才能开始输出。因此,该sort程序可以轻松地提供就地修改文件的选项:

sort temp.txt -o temp.txt

具体来说,GNU 的文档sort说:

通常, sort 在打开输出文件之前读取所有输入,因此您可以使用 和 等命令安全地对文件进行sort -o F F排序cat F | sort -o F。但是,sortwith --merge( -m) 可以在读取所有输入之前打开输出文件,因此类似命令cat F | sort -m -o F - G是不安全的,因为 sort 可能在读取完成F之前开始写入cat

虽然 BSD 的文档sort说:

如果 [the] 输出文件是输入文件之一,则 sort 在排序之前将其复制到一个临时文件并将输出写入 [the] 输出文件。

诸如此类的命令uniq可以在完成读取输入之前开始写入输出。这些命令通常不支持就地编辑(它们更难支持此功能)。

您通常使用临时文件来解决此问题,或者如果您绝对希望避免使用中间文件,则可以使用缓冲区来存储完整的结果,然后再将其写出。例如,使用perl

uniq temp.txt | perl -e 'undef $/; $_ = <>; open(OUT,">temp.txt"); print OUT;'

uniq在这里,perl 部分从in 变量中读取完整的输出$_,然后用该数据覆盖原始文件。你可以用你选择的脚本语言做同样的事情,甚至可以用 Bash。但请注意,它需要足够的内存来存储整个文件,这在处理大文件时是不可取的。

于 2008-09-29T07:40:28.537 回答
19

这是一种更通用的方法,适用于 uniq、sort 等。

{ rm file && uniq > file; } < file
于 2010-12-10T22:39:27.947 回答
11

东武对海绵的评论本身就是一个答案。

引用moreutils主页:

到目前为止,moreutils 中最通用的工具可能是海绵(1),它可以让你做这样的事情:

% sed "s/root/toor/" /etc/passwd | grep -v joey | sponge /etc/passwd

然而,史蒂夫杰索普在这里评论sponge同样的问题。如果之前管道中的任何命令失败,则将覆盖原始文件。sponge

$ mistyped_command my-important-file | sponge my-important-file
mistyped-command: command not found

哦哦,my-important-file没了。

于 2013-07-05T14:49:13.847 回答
6

给你,一行:

sort temp.txt > temp.txt.sort && mv temp.txt.sort temp.txt

从技术上讲,没有复制到临时文件,“mv”命令应该是即时的。

于 2008-09-28T18:35:10.390 回答
4

许多人提到了-o选项。这是手册页部分。

从手册页:

   -o output-file
          Write output to output-file instead of to the  standard  output.
          If  output-file  is  one of the input files, sort copies it to a
          temporary file before sorting and writing the output to  output-
          file.
于 2008-09-28T20:01:31.043 回答
4

我喜欢这个sort file -o file答案,但不想两次键入相同的文件名。

使用 BASH历史扩展

$ sort file -o !#^

当您按下 时,获取当前行的第一个参数enter

独特的就地排序:

$ sort -u -o file !#$

获取当前行中的最后一个参数。

于 2013-07-11T21:22:10.527 回答
3

这将受到高度内存限制,但您可以使用 awk 将中间数据存储在内存中,然后将其写回。

uniq temp.txt | awk '{line[i++] = $0}END{for(j=0;j<i;j++){print line[j]}}' > temp.txt
于 2008-09-29T13:40:27.907 回答
3

sponge更常见的替代方法sed

sed -ni r<(command file) file

它适用于任何命令(sort, uniq, tac, ...),并使用众所周知sed的 's-i选项(就地编辑文件)。

警告:首先尝试command file,因为就地编辑文件本质上是不安全的。


解释

首先,您要告诉sed不要打印(原始)行(-n选项),并且在sed' rcommandbash' Process Substitution的帮助下,生成的内容<(command file)将是保存在原地的输出。


让事情变得更容易

您可以将此解决方案包装成一个函数:

ip_cmd() { # in place command
    CMD=${1:?You must specify a command}
    FILE=${2:?You must specify a file}
    sed -ni r<("$CMD" "$FILE") "$FILE"
}

例子

$ cat file
d
b
c
b
a

$ ip_cmd sort file
$ cat file
a
b
b
c
d

$ ip_cmd uniq file
$ cat file
a
b
c
d

$ ip_cmd tac file
$ cat file
d
c
b
a

$ ip_cmd
bash: 1: You must specify a command
$ ip_cmd uniq
bash: 2: You must specify a file
于 2015-01-17T16:10:11.027 回答
2

阅读非交互式编辑器,ex.

于 2008-09-28T18:33:38.570 回答
1

使用参数--output=-o

刚刚在 FreeBSD 上试过:

sort temp.txt -otemp.txt
于 2008-09-28T18:49:19.763 回答
1

要添加该uniq功能,有哪些缺点:

sort inputfile | uniq | sort -o inputfile
于 2010-03-17T14:50:24.960 回答
0

如果您坚持使用该sort程序,则必须使用中间文件-我认为没有sort在内存中排序的选项。stdin/stdout 的任何其他技巧都将失败,除非您可以保证 sort 的 stdin 的缓冲区大小足以容纳整个文件。

编辑:对我感到羞耻。sort temp.txt -o temp.txt效果很好。

于 2008-09-28T18:49:35.210 回答
0

另一种解决方案:

uniq file 1<> file
于 2016-07-27T10:49:15.570 回答