我试图删除除最后一行之外的所有文件行但是以下命令不起作用,尽管file.txt不为空。
$cat file.txt |tail -1 > file.txt
$cat file.txt
为什么会这样?
答案 0 :(得分:19)
从文件通过管道重定向回同一文件是不安全的;如果file.txt
在tail
开始读取第一个阶段之前设置管道的最后一个阶段时,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
答案 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时,它会执行以下操作:
当'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”将删除磁盘上的引用,然后subshell将其打开以写入“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读取空文件,因为重写将在 管道中的任何命令执行之前发生。