Bash输出重定向的问题

linux bash

5201 观看

11回复

0 作者的声誉

我试图删除文件中除最后一行以外的所有行,但是以下命令不起作用,尽管file.txt不为空。

$cat file.txt |tail -1 > file.txt

$cat file.txt

为什么会这样呢?

作者: Aman Jain 的来源 发布者: 2008 年 9 月 23 日

回应 (11)


0

131 作者的声誉

您似乎不喜欢将其写回到相同的文件名。如果您执行以下操作,它将起作用:

$cat file.txt | tail -1 > anotherfile.txt
作者: Lewis Baumstark 发布者: 23.09.2008 07:31

19

196360 作者的声誉

决定

通过流水线从文件重定向回同一文件是不安全的;如果file.txttail开始读取第一级之前设置管道的最后一级时,shell覆盖了该外壳,则最终输出为空。

而是执行以下操作:

tail -1 file.txt >file.txt.new && mv file.txt.new file.txt

...嗯,实际上,不要在生产代码中这样做;特别是如果您在对安全性敏感的环境中并以root用户身份运行,则以下更合适:

tempfile="$(mktemp file.txt.XXXXXX)"
chown --reference=file.txt -- "$tempfile"
chmod --reference=file.txt -- "$tempfile"
tail -1 file.txt >"$tempfile" && mv -- "$tempfile" file.txt

另一种方法(避免使用临时文件,除非<<<在您的平台上隐式创建它们)是:

lastline="$(tail -1 file.txt)"; cat >file.txt <<<"$lastline"

(上述实现是特定于bash的,但是可以在回声不起作用的情况下使用,例如最后一行包含“ --version”时)。

最后,可以使用moreutils中的海绵:

tail -1 file.txt | sponge file.txt
作者: Charles Duffy 发布者: 23.09.2008 07:33

1

8885 作者的声誉

正如Lewis Baumstark所说,您不喜欢写入相同的文件名。

这是因为外壳程序会在运行“ cat file.txt”之前打开“ file.txt”并将其截断以进行重定向。所以,你必须

tail -1 file.txt > file2.txt; mv file2.txt file.txt
作者: wnoise 发布者: 23.09.2008 07:34

0

9034 作者的声誉

tail -1 > file.txt将会覆盖您的文件,导致cat读取一个空文件,因为在执行管道中的任何命令之前都会进行重写。

作者: dsm 发布者: 23.09.2008 07:34

3

162447 作者的声誉

在执行“ cat”之前,Bash已经打开“ file.txt”进行写入,清除了其内容。

通常,不要在同一条语句中写入要读取的文件。如上所述,可以通过写入不同的文件来解决此问题:

$ cat file.txt | 尾巴-1> anotherfile.txt
$ mv anotherfile.txt file.txt
或使用moreutils中的海绵等实用程序:
$ cat file.txt | 尾巴-1 | 海绵file.txt
这是可行的,因为海绵在打开其输出文件之前一直等到其输入流结束。

作者: ephemient 发布者: 23.09.2008 07:39

2

13931 作者的声誉

当您将命令字符串提交给bash时,它将执行以下操作:

  1. 创建一个I / O管道。
  2. 启动“ / usr / bin / tail -1”,从管道中读取并写入file.txt。
  3. 启动“ / usr / bin / cat file.txt”,写入管道。

到“猫”开始阅读时,“ file.txt”已被“ tail”截断。

这就是Unix和shell环境设计的全部内容,并且一直追溯到原始的Bourne shell。“这是功能,而不是错误。

作者: Craig Trader 发布者: 23.09.2008 07:43

2

53541 作者的声誉

tmp = $(tail -1 file.txt); echo $ tmp> file.txt;

作者: Ken 发布者: 23.09.2008 07:44

5

144 作者的声誉

您可以使用sed删除文件中除最后一行以外的所有行:

sed -i '$!d' file
  • -i告诉sed将文件替换到位;否则,结果将写入STDOUT。
  • $是与文件最后一行匹配的地址。
  • d是删除命令。在这种情况下,它被否定,因此所有与地址匹配的行都将被删除。
作者: Chris 发布者: 24.09.2008 02:56

2

978 作者的声誉

这在Linux shell中效果很好:

replace_with_filter() {
  local filename="$1"; shift
  local dd_output byte_count filter_status dd_status
  dd_output=$("$@" <"$filename" | dd conv=notrunc of="$filename" 2>&1; echo "${PIPESTATUS[@]}")
  { read; read; read -r byte_count _; read filter_status dd_status; } <<<"$dd_output"
  (( filter_status > 0 )) && return "$filter_status"
  (( dd_status > 0 )) && return "$dd_status"
  dd bs=1 seek="$byte_count" if=/dev/null of="$filename"
}

replace_with_filter file.txt tail -1

dd的“ notrunc”选项用于将过滤后的内容写回原位,而dd再次需要(带有字节数)来实际截断文件。如果新文件的大小大于或等于旧文件的大小,dd则无需第二次调用。

与文件复制方法相比,此方法的优点是:1)不需要额外的磁盘空间; 2)大文件上的性能更快; 3)纯外壳(dd除外)。

作者: m104 发布者: 25.03.2010 03:56

1

6854 作者的声誉

仅在这种情况下,可以使用

 file.txt)
这将在子目录“(...)”中连接“ cat”之前打开“ file.txt”。“ rm file.txt”将从子磁盘上删除引用,然后subshel​​l将其打开以写入“ tail”,但是通过打开的描述符,其内容仍然可用,该描述符传递给“ cat”,直到关闭stdin。因此,您最好确保此命令将完成,否则“ file.txt”的内容将丢失

作者: ony 发布者: 25.03.2010 05:15

1

242686 作者的声誉

echo "$(tail -1 file.txt)" > file.txt
作者: ghostdog74 发布者: 25.03.2010 05:24
32x32