Bash输出重定向问题

时间:2008-09-23 19:29:27

标签: linux bash

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

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

$cat file.txt

为什么会这样?

11 个答案:

答案 0 :(得分:19)

从文件通过管道重定向回同一文件是不安全的;如果file.txttail开始读取第一个阶段之前设置管道的最后一个阶段时,shell会覆盖tail -1 file.txt >file.txt.new && mv file.txt.new file.txt ,那么最终会出现空输出。

请执行以下操作:

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

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

<<<

另一种方法(避免临时文件,除非lastline="$(tail -1 file.txt)"; cat >file.txt <<<"$lastline" 在您的平台上隐式创建它们)如下:

tail -1 file.txt | sponge file.txt

(上面的实现是特定于bash的,但适用于echo不能的情况 - 例如,当最后一行包含“--version”时)。

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

{{1}}

答案 1 :(得分:5)

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

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

答案 2 :(得分:3)

在'cat'执行之前,Bash已经打开'file.txt'进行写作,清除其内容。

通常,请勿在同一语句中写入您正在阅读的文件。这可以通过写入不同的文件来解决,如上所述:

$cat file.txt | tail -1 >anotherfile.txt
$mv anotherfile.txt file.txt
或使用来自moreutils的海绵等实用程序:
$cat file.txt | tail -1 | sponge file.txt
这是有效的,因为在打开输出文件之前,海绵会一直等到输入流结束。

答案 3 :(得分:2)

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

  1. 创建I / O管道。
  2. 启动“/ usr / bin / tail -1”,从管道读取,并写入file.txt。
  3. 启动“/ usr / bin / cat file.txt”,写入管道。
  4. 当'cat'开始阅读时,'file.txt'已被'tail'截断。

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

答案 4 :(得分:2)

tmp = $(tail -1 file.txt); echo $ tmp&gt; file.txt的;

答案 5 :(得分:2)

这在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)纯shell(除了dd)。

答案 6 :(得分:1)

正如路易斯·鲍姆斯塔克所说,它不喜欢你写的是同一个文件名。

这是因为shell打开“file.txt”并截断它以在运行“cat file.txt”之前执行重定向。所以,你必须

tail -1 file.txt > file2.txt; mv file2.txt file.txt

答案 7 :(得分:1)

就这种情况而言,可以使用

cat < file.txt | (rm file.txt; tail -1 > file.txt)
这将在“cat”与“(...)”中的子shell连接之前打开“file.txt”。 “rm file.txt”将删除磁盘上的引用,然后subshel​​l将其打开以写入“tail”,但内容仍将通过打开的描述符提供,该描述符将传递给“cat”,直到它将关闭stdin。因此,您最好确保此命令完成或“file.txt”的内容将丢失

答案 8 :(得分:1)

echo "$(tail -1 file.txt)" > file.txt

答案 9 :(得分:0)

似乎不喜欢你把它写回同一个文件名的事实。如果您执行以下操作,则可以:

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

答案 10 :(得分:0)

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