如何使用bash“在适当位置”执行任何编辑其文件(参数)的命令?


110

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

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

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

sortx temp.txt > temp.txt

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


编辑:该-o选项对于非常酷sort。我sort以我的问题为例。我在其他命令中遇到了同样的问题:

uniq temp.txt > temp.txt.

有更好的一般解决方案吗?


Answers:


171
sort temp.txt -o temp.txt

3
这是一个答案。我实际上在想是否可以解决此问题。例如,如果我想在文件“就地”中找到所有UNIQ行,则无法执行-o
jm。

它不是通用的,但是您可以将-u与GNU排序一起使用以找到唯一的行
James 2010年

有没有人解决问题允许的sort --inplace *.txt?那
真是太

@sehe尝试:find . -name \*.txt -exec sort {} -o {} \;
Keith Gaughan

29

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

sort temp.txt -o temp.txt

具体来说,GNU文档sort说:

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

虽然BSD的文档sort说:

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

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

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

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

在这里,perl部分从uniqin变量中读取完整的输出$_,然后用此数据覆盖原始文件。您甚至可以使用Bash使用您选择的脚本语言执行相同的操作。但是请注意,它将需要足够的内存来存储整个文件,在处理大文件时不建议这样做。


19

这是更通用的方法,可与uniq,sort和whatnot一起使用。

{ rm file && uniq > file; } < file

14
sponge来自moreutils的另一种通用方法:cat file |frobnicate |sponge file
东武

3
@Tobu:为什么不将其作为单独的答案提交?
Flimm

1
可能需要注意的是,这不一定会保留文件权限。您的umask决定了新的权限。
2014年

1
整rick。您能解释一下它是如何工作的吗?
patryk.beza 2015年

2
@ patryk.beza:顺序:从原始文件中打开输入FD。原始目录条目被删除;处理重定向,创建一个新的空文件,其名称与以前的旧文件相同;然后命令运行。
查尔斯·达菲

10

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

要引用moreutils主页:

到目前为止,moreutils中最通用的工具可能是海绵(1),它可以让您执行以下操作:

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

但是,史蒂夫·杰索普(Steve Jessop)在这里发表sponge了同样的评论。如果之前管道中的任何命令sponge失败,则原始文件将被覆盖。

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

呃,my-important-file走了。


1
Sponge知道它将用于替换输入文件,并且最初会创建一个临时文件以避免出现竞争情况。为了使它起作用,海绵必须是管道中的最后一个元素,并且必须允许海绵本身创建输出文件(例如,与shell级别的输出重定向相反)。顺便说一句:似乎对于“失败”情况而言,一个简单的源代码修补程序是在发生管道失败的情况下不重命名临时文件(不知道为什么海绵没有该选项)。
布伦特·布拉德本

我认为,如果你添加set -o pipefail在你的脚本,错误的开始上mistyped_command my-important-file会立即使脚本退出,执行前sponge,从而保留重要文件。
Elouan Keryell-Even,

6

在这里,一行:

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

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


6
嗯 我仍将temp.txt.sort称为临时文件。
JesperE

5
该代码具有风险,因为如果排序由于任何原因而失败而未完成其工作,则原始代码将被覆盖。
史蒂夫·杰索普

1
磁盘空间不足是一个合理的原因,或者是一个信号(用户按下CTRL-C)。
史蒂夫·杰索普

5
如果您想使用类似这样的内容,请使用&&(逻辑和)而不是; 因为使用该命令将确保如果命令失败,则不会执行下一个命令。例如:cp backup.tar /root/backup.tar && rm backup.tar如果您没有复制权限,则将是安全的,因为该文件不会被删除
丹尼尔斯

1
更改了我的答案以考虑您的建议,谢谢
davr

4

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

使用BASH 历史记录扩展

$ sort file -o !#^

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

就地独特排序:

$ sort -u -o file !#$

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


3

许多人提到了-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.

3

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

uniq temp.txt | awk '{line[i++] = $0}END{for(j=0;j<i;j++){print line[j]}}' > temp.txt

我认为有可能>命令(uniq在这种情况下)读取文件之前将文件截断。
马丁

3

替代sponge与更常见sed

sed -ni r<(command file) file

它适用于任何命令(sortuniqtac,...),并使用了非常著名sed-i选项(编辑文件就地)。

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


说明

首先,你告诉sed不打印(原)线(-n选件),并与的帮助sedr命令bash进程替换,通过所产生的内容<(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

1

使用参数--output=-o

刚刚在FreeBSD上尝试过:

sort temp.txt -otemp.txt

虽然是正确的,它只是一个重复这个答案
whoan

1

要添加uniq功能,不利之处在于:

sort inputfile | uniq | sort -o inputfile


0

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

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


我也把Q看成是“就地”,但二读让我相信他并不是真的在要求
epatel

0

另一个解决方案:

uniq file 1<> file

应当注意,该<>技巧仅在这种情况下有效,因为uniq特殊之处在于它仅将输入行复制到输出行,并在途中掉线。如果使用了其他命令(例如sed)来更改输入(例如,将每个都更改aaa),那么只要file输入足够大(大于单个读取缓冲区)。
大卫,
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.