Linux:如何同时使用一个文件作为input和输出?

我刚刚在bash中运行以下内容:

uniq .bash_history > .bash_history 

我的历史档案完全空了。

我想我需要一种方法来在写入之前阅读整个文件。 这是怎么做的?

PS:我明显想到使用一个临时文件,但我正在寻找一个更优雅的解决scheme。

我build议使用moreutils的 sponge 。 从手册:

 DESCRIPTION sponge reads standard input and writes it out to the specified file. Unlike a shell redirect, sponge soaks up all its input before opening the output file. This allows for constructing pipelines that read from and write to the same file. 

要将此应用于您的问题,请尝试:

 uniq .bash_history | sponge .bash_history 

我只是想贡献一个简单的答案,而不使用海绵(因为它通常不包含在轻量级环境中)。

 echo "$(uniq .bash_history)" > .bash_history 

应该有理想的结果。 子shell在执行之前.bash_history打开写入。 正如Phil P的回答中所解释的那样,在原始命令中读取.bash_history时,它已经被'>'运算符截断。

问题是你的shell在运行这些命令之前正在设置命令pipe道。 这不是“input和输出”的问题,而是在uniq运行之前文件的内容已经消失了。 它是这样的:

  1. shell打开>输出文件进行写入,将其截断
  2. shell设置为文件描述符1(用于标准输出)用于该输出
  3. shell执行uniq,或许像execlp(“uniq”,“uniq”,“.bash_history”,NULL)
  4. uniq运行,打开.bash_history,什么也没有find

有很多解决scheme,包括其他人提到的就地编辑和临时文件使用,但关键是要了解问题,实际发生了什么问题,为什么。

使用moreutils的海绵

 uniq .bash_history | sponge .bash_history 

另一个不使用sponge是使用以下命令:

 { rm .bash_history && uniq > .bash_history; } < .bash_history 

这是在backreference.org 文件的“就地”编辑文件中描述的作弊手段之一。

它基本上打开文件阅读,然后“删除”它。 但是,它并没有真正被删除:有一个打开的文件描述符指向它,只要保持打开状态,文件仍然存在。 然后它创build一个具有相同名称的新文件,并将唯一的行写入它。

这个解决scheme的缺点:如果uniq由于某种原因失败,你的历史将会消失。

这个sed脚本删除相邻的重复项。 使用-i选项,它会就地进行修改。 它来自sed info文件:

 sed -i 'h;:b;$b;N;/^\(.*\)\n\1$/ {g;bb};$b;P;D' .bash_history 

作为一个有趣的消息,sed也使用临时文件(这只是为你做的):

 $ strace sed -i 's/foo/bar/g' foo open("foo", O_RDONLY|O_LARGEFILE) = 3 ... open("./sedPmPv9z", O_RDWR|O_CREAT|O_EXCL|O_LARGEFILE, 0600) = 4 ... read(3, "foo\n"..., 4096) = 4 write(4, "bar\n"..., 4) = 4 read(3, ""..., 4096) = 0 close(3) = 0 close(4) = 0 rename("./sedPmPv9z", "foo") = 0 close(1) = 0 close(2) = 0 

描述:
tempfile ./sedPmPv9z变成fd 4, foo文件变成fd 3.读取操作在fd 3上,在fd 4(临时文件)上写入。 在重命名调用中,foo文件将被临时文件覆盖。

一个临时文件是非常多的,除非有问题的命令恰好支持就地编辑( uniq不 – 有些sed s do( sed -i ))。

您可以在Ex模式下使用Vim:

 ex -sc '%!uniq' -cx .bash_history 
  1. %select所有行

  2. ! 运行命令

  3. 保存并closures

另一个scheme

 uniq file 1<> líneas.txt 

您也可以使用tee,使用uniq输出作为input:

 uniq .bash_history | tee .bash_history