我尝试在文件中保留最后 50 行,每分钟保存一次温度。我使用了这个命令:
tail -n 50 /home/pi/Documents/test > /home/pi/Documents/test
但结果是空的测试文件。我想,它会列出测试文件的最后 50 行并将其插入到测试文件中。当我使用这个命令时:
tail -n 50 /home/pi/Documents/test > /home/pi/Documents/test2
它工作正常。 test2 文件中有 50 行。
有人能解释一下问题出在哪里吗?
答案1
问题是您的 shell 在运行命令之前设置命令管道。这不是“输入和输出”的问题,而是文件的内容在 tail 运行之前就已经消失了。它是这样的:
- shell 打开
>
输出文件进行写入,并截断它 - shell 设置文件描述符 1(用于标准输出)用于该输出
- 外壳执行
tail
. tail
运行,打开/home/pi/Documents/test
,发现什么也没有
解决方案有很多种,但关键是要了解问题所在、到底出了什么问题以及原因。
这将产生您正在寻找的东西,
echo "$(tail -n 50 /home/pi/Documents/test)" > /home/pi/Documents/test
解释 :
$()
称为命令替换,执行tail -n 50 /home/pi/Documents/test
- 引号在输出中保留换行符。
> /home/pi/Documents/test
将 的输出重定向echo "$(tail -n 50 /home/pi/Documents/test)"
到同一文件。
答案2
文件重定向首先清除文件的另一个解决方案是sponge
使用moreutils
像这样打包:
tail -n 50 /home/pi/Documents/test | sponge /home/pi/Documents/test
答案3
这是因为 bash 首先处理重定向>
,删除文件的内容。然后它执行命令。如果您使用>>
,最后 50 行将被附加到文件中当前内容的末尾。在本例中,相同的 50 行会重复两次。
当重定向到不同的文件时,该命令按预期工作。以下是将文件的最后 50 行写入同名文件的一种方法:
tail -50 /home/pi/Documents/test > /home/pi/Documents/test2 && mv /home/pi/Documents/test2 /home/pi/Documents/test
这首先将最后 50 行写入临时文件,然后将其移动mv
以替换原始文件。
正如评论中所指出的,如果文件仍然打开,这将不起作用。移动文件还会创建新的索引节点,并可能更改所有权和权限。使用临时文件执行此操作的更好方法是:
tail -50 /home/pi/Documents/test > /home/pi/Documents/test2 ; cat /home/pi/Documents/test2 > /home/pi/Documents/test
临时文件也可以被删除,尽管每次发生这种情况时其内容都会被覆盖。
答案4
既然您已经了解了 shell 重定向的主要问题,那么这里有另一种方法可以将文件修剪到最后 50 行:
file=/path/to/the/file
n=$(( $(wc -l < "$file") - 50 ))
[[ $n -gt 0 ]] && sed -i 1,${n}d "$file"
这项艰苦的工作是由 (GNU) sed 的-i
“就地编辑”功能完成的,该功能通过在临时文件中创建输出来在幕后工作。其余行设置 sed 操作的数学,即:
- 计算文件中的行数 (
wc
),然后减去 50;将其分配给n
. - 如果
n
为正,则运行 sed 命令删除第 1 行到第 n 行。