我试图移除所有的线路的一个文件除了最后一行,但以下命令没有工作,尽管file.txt 不是空的。

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

$cat file.txt

为什么会这样呢?

有帮助吗?

解决方案

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

请执行以下操作:

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的,但是在echo不能的情况下工作 - 例如当最后一行包含<!> quot; - version <!> quot;时)。

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

tail -1 file.txt | sponge file.txt

其他提示

你可以使用sed删除所有线路,但最后文件:

sed -i '$!d' file
  • -我 告诉sed替换的文件;否则,结果将写信给STDOUT。
  • $ 是的地址相匹配的最后一线的文件。
  • d 是删除的命令。在这种情况下,它是否定的 !, ,因此,所有的线路 匹配的地址将予以删除。

在'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
这是有效的,因为在打开输出文件之前,海绵会一直等到输入流结束。

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

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

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

tmp = $(tail -1 file.txt); echo $ tmp <!> gt; file.txt的;

这在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 <!>”;选项用于将过滤后的内容写回原位,而再次需要<=>(带字节数)以实际截断文件。如果新文件大小大于或等于旧文件大小,则不需要第二次<=>调用。

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

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

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

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

只是对于这种情况下,它可以使用

cat < file.txt | (rm file.txt; tail -1 > file.txt)
即将打开"file.txt"之前的连接"猫"与子shell在"(...)"."rm file.txt"将删除的参考从磁盘之前,子shell将打开它写为"尾",但内容将仍可通过打开的描述符其传递给"猫"直到将近stdin。所以你最好能确保这种命令将完成或内容的"file.txt"将会丢失

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

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

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

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top