如何弹出(读取和删除)文件的一行?

pLu*_*umo 6 sed text-processing

我正在尝试获取文件的最后一行,然后将其删除:

阅读该行:

sed -n '$p' file
# or
tail -n 1 file
Run Code Online (Sandbox Code Playgroud)

删除该行:

sed -i '$d' file
Run Code Online (Sandbox Code Playgroud)

这是有效的,但是有没有办法只用一个命令来做到这一点?

mos*_*svy 11

无论流行的智慧是什么,为了切断最后一行而每次都必须读取整个文件是愚蠢的,无论它是在单个进程/命令中完成还是在两个或三个中完成。

不像sed -i,tail足够聪明,不会从头开始读取文件以确定它的最后一行是什么;此外,linux 有一个truncate(1)实用程序,它与tail允许您在 O(1) 时间内“弹出”一个大文件的最后一行:

# usage popline file [line_count, 1 by default]
popline()(LC_CTYPE=C; l=`tail -n "${2:-1}" "$1"; echo t`; l=${l%t}; truncate -s "-${#l}" "$1"; printf %s "$l")

$ wc -l /tmp/foo
3579500 /tmp/foo
$ cp /tmp/foo /tmp/foo1 && time sed -i '$d' /tmp/foo1

real    0m1.077s
user    0m0.457s
sys     0m0.156s
$ cp /tmp/foo /tmp/foo1 && time popline /tmp/foo1
 */

real    0m0.052s
user    0m0.002s
sys     0m0.003s
Run Code Online (Sandbox Code Playgroud)

  • @MikeHill 命令替换,如 shell 中的 `\`...\`` 总是去掉尾随的换行符,如果没有换行符,这是保留它而不添加换行符的技巧。请参阅 [此处](/sf/answers/1062909011/) 以获得完整的解释。 (2认同)
  • 文件是字节的集合,而不是行;在最坏的情况下它真的是 O(n),因为最后一行可能是整个文件,你必须读取 *所有 * 字节来确定。在*实践*中,你只需要读取这些字节的一小部分,所以它“感觉”像 O(1)。(此外,您不必缓冲超过 X 个字节;一旦找到特定块中的最后一个换行符,您可以简单地重新读取此后的每个字节并将它们直接写入标准输出。) (2认同)

Kus*_*nda 7

目标是让一个命令输出文件的最后一行,同时从原始文件中删除该行。

sed -i -e '${w /dev/stdout' -e 'd;}' file
Run Code Online (Sandbox Code Playgroud)

这将运行以下sed脚本:

${
    w /dev/stdout
    d
}
Run Code Online (Sandbox Code Playgroud)

这将写入最后一行/dev/stdout,然后将其删除。所有其他行都通过-i选项写回原始文件。

命令行上的脚本必须一分为二,因为无法以其他方式分隔w命令的输出文件名(除了插入文字换行符)。

ed

ed -s file <<END_ED
p
d
w
END_ED
Run Code Online (Sandbox Code Playgroud)

ed打开文件file并将光标放在文件的最后一行。第一个命令将该行打印到标准输出,第二个命令删除它,最后一个命令将缓冲区写回文件。ed以这种方式使用可能不适用于大文件。