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

Mil*_*ver 68 linux bash

我刚刚在 bash 中运行了以下命令:

uniq .bash_history > .bash_history
Run Code Online (Sandbox Code Playgroud)

我的历史文件最终完全是空的。

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

PS:我显然想过使用临时文件,但我正在寻找更优雅的解决方案。

Har*_*mha 92

echo "$(uniq .bash_history)" > .bash_history
Run Code Online (Sandbox Code Playgroud)

应该有想要的结果。子shell在.bash_history打开写入之前被执行。正如上文菲尔普的回答,通过的时间.bash_history是在原来的命令来读取,它已经被截断>操作

  • 如果 `echo` 中的命令引发错误,这将清除文件 (2认同)

jld*_*ger 54

我建议使用spongefrom moreutils。从联机帮助页:

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.
Run Code Online (Sandbox Code Playgroud)

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

uniq .bash_history | sponge .bash_history
Run Code Online (Sandbox Code Playgroud)

  • 它就像猫,但具有吸吮能力:D (8认同)

Phi*_*l P 15

问题是您的 shell 在运行命令之前设置了命令管道。这不是“输入和输出”的问题,而是文件的内容在 uniq 甚至运行之前就已经消失了。它是这样的:

  1. shell 打开>输出文件进行写入,截断它
  2. shell 设置为将文件描述符 1(用于标准输出)用于该输出
  3. shell 执行 uniq,可能类似于 execlp("uniq", "uniq", ".bash_history", NULL)
  4. uniq 运行,打开 .bash_history 并在那里找不到任何东西

有多种解决方案,包括其他人提到的就地编辑和临时文件使用,但关键是要了解问题、实际出现的问题以及原因。


scy*_*scy 13

不使用sponge,执行此操作的另一个技巧是以下命令:

{ rm .bash_history && uniq > .bash_history; } < .bash_history
Run Code Online (Sandbox Code Playgroud)

这是 backreference.org 上优秀文章“就地”文件编辑中描述的作弊之一。

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

此解决方案的缺点:如果uniq由于某种原因失败,您的历史记录将消失。


Jus*_*tin 6

使用moreutils 的海绵

uniq .bash_history | sponge .bash_history
Run Code Online (Sandbox Code Playgroud)