IO重定向和head命令

IO重定向和head命令

今天我试图.hgignore从 Cygwin bash shell 快速编辑一个文件,但我添加了一行错误的内容。我不确定这是否是最好的方法,但我很快想到使用head -1 .hgignore删除有问题的行(我以前在文件中只有一行)。果然,执行时它给出第一行作为唯一的输出。

但是当我尝试重定向输出并使用 重写文件时head -1 .hgignore > .hgignore,该文件是空的。为什么会出现这种情况?如果我尝试附加,head -1 .hgignore >> .hgignore它会正确附加,但这显然不是所需的结果。为什么截断重定向在这种情况下不起作用?

答案1

我想布鲁斯会回答这里发生了什么与外壳管道。

我最喜欢的小实用程序之一是sponge来自的命令更多实用程序。它通过在打开目标输出文件并写入数据之前“吸收”所有可用输入来解决这个问题。它允许您完全按照您的预期编写管道:

$ head -1 .hgignore | sponge .hgignore

穷人的解决方案是将输出通过管道传输到临时文件,然后在管道完成后(例如您运行的下一个命令)将临时文件移回原始文件位置。

$ head -1 .hgingore > .hgignore.tmp
$ mv .hgignore{.tmp,}

答案2

当 shell 获取如下命令行时:command > file.outshell 本身会打开(并且可能创建)名为file.out. shell 将文件描述符 0 设置为它从 open 获得的文件描述符。这就是 I/O 重定向的工作原理:每个进程都知道文件描述符 0、1 和 2。

最困难的部分是如何打开file.out。大多数时候,您希望file.out在偏移量 0 处打开写入(即截断),这就是 shell 为您所做的。它截断了 .hgignore,打开它进行写入,将文件描述符复制为 0,然后执行 exec head。即时文件破坏。

在 bash shell 中,您可以执行 aset noclobber来更改此行为。

答案3

head -n 1 file > file

filehead在开始之前被截断,但是如果你写它:

head -n 1 file 1<> file

它不是file以读写模式打开的。但是,当head完成写入时,它不会截断文件,因此上面的行将是无操作(head只会重写第一行本身并保持其他行不变)。

但是, afterhead返回后,当fd仍然打开时,您可以调用另一个执行truncate.

例如:

{ head -n 1 file; perl -e 'truncate STDOUT, tell STDOUT'; } 1<> file

这里重要的是truncate,上面的内容head只是将光标移动到文件内第一行之后的 fd 1 处。它确实重写了我们不需要的第一行,但这没有害处。

有了 POSIX 头,我们实际上可以不用重写第一行就可以逃脱:

{ head -n 1 > /dev/null
  perl -e 'truncate STDIN, tell STDIN'
} <> file

head在这里,我们使用在标准输入中移动光标位置的事实。虽然head通常会通过大块读取输入来提高性能,但 POSIX 会要求它(如果可能)seek在第一行超出时立即返回。但请注意,并非所有实现都这样做。

read或者,在这种情况下,您可以使用 shell 的命令:

{ read -r dummy; perl -e 'truncate STDIN, tell STDIN'; } <> file

答案4

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

ex -sc '2,d|x' .hgignore
  1. 2,选择第 2 行直到末尾

  2. d删除

  3. x保存并关闭

相关内容