如何使用bash/sed脚本删除文本文件的第一行?

Bre*_*nt 517 bash scripting sed

我需要使用bash脚本从一个巨大的文本文件中重复删除第一行.

现在我正在使用sed -i -e "1d" $FILE- 但删除大约需要一分钟.

有没有更有效的方法来实现这一目标?

Aar*_*lla 962

试试GNU尾巴:

tail -n +2 "$FILE"
Run Code Online (Sandbox Code Playgroud)

-n x:只需打印最后x一行.tail -n 5会给你输入的最后5行.该+标志那种反转的争论,使tail打印任何东西,但第一x-1线.tail -n +1将打印整个文件,tail -n +2除第一行外的所有内容等.

GNU tail要快得多sed.tail也可以在BSD上使用,并且-n +2两个工具的标志是一致的.查看FreeBSDOS X手册页了解更多信息.

不过,BSD版本可能要慢很多sed.我想知道他们是如何做到的; tail应该只是逐行读取文件,同时sed执行相当复杂的操作,包括解释脚本,应用正则表达式等.

注意:您可能很想使用

# THIS WILL GIVE YOU AN EMPTY FILE!
tail -n +2 "$FILE" > "$FILE"
Run Code Online (Sandbox Code Playgroud)

但这会给你一个空文件.原因是重定向(>)发生在tailshell调用之前:

  1. Shell截断文件 $FILE
  2. Shell为其创建了一个新流程 tail
  3. Shell将tail进程的stdout重定向到$FILE
  4. tail 从现在空的读取 $FILE

如果要删除文件中的第一行,则应使用:

tail -n +2 "$FILE" > "$FILE.tmp" && mv "$FILE.tmp" "$FILE"
Run Code Online (Sandbox Code Playgroud)

&&将确保在出现问题时不会覆盖该文件.

  • @Eddie:user869097表示当*single*行为15Mb或更多时,它不起作用.只要行更短,`tail`将适用于任何文件大小. (41认同)
  • 它不适用于15Mb或更多的行 (33认同)
  • @Dreampuf - 来自手册页:` - n N表示输出最后N行,而不是最后10行; 或使用+ N输出以Nth`开头的行 (17认同)
  • 我打算同意@JonaChristopherSahnwaldt - 尾巴比sed变种慢很多,比一个数量级要慢得多.我正在一个500,000K行的文件上测试它(每行不超过50个字符).然而,我意识到我使用的是FreeBSD版本的tail(默认情况下附带OS X).当我切换到GNU尾部时,尾部调用比sed调用快10倍(以及GNU sed调用).如果你正在使用GNU,那么AaronDigulla在这里是正确的. (9认同)
  • 你能解释一下这些论点吗? (6认同)
  • @user:有趣的一点.你从哪里得到这个号码? (3认同)
  • 根据这个http://ss64.com/bash/tail.html,当使用BSD'tail'和`-r`选项时,典型的缓冲区默认为32k.也许系统中某处有缓冲区设置?或者`-n`是一个32位有符号数? (3认同)
  • 哎呀.谢谢你纠正我.WO,15mb线..我甚至无法想象这样的情况. (2认同)
  • @Dreampuf:`sed`有一个当前行的内部缓冲区,而`tail`可以通过记住N个最后一个换行符的偏移来消失(注意我实际上没有查看源代码). (2认同)

小智 161

您可以使用-i更新文件,而无需使用">"运算符.以下命令将从文件中删除第一行并将其保存到文件中.

sed -i '1d' filename
Run Code Online (Sandbox Code Playgroud)

  • 这每次都有效,真的应该是最好的答案! (10认同)
  • 请记住,Mac 需要在将 sed 与就地编辑一起使用时提供后缀。所以用 -i.bak 运行上面的 (4认同)
  • 这个版本比`tail -n + 2`更可读,更通用.不确定为什么它不是最佳答案. (4认同)
  • 只需注意-要删除几行,请使用`sed -i'1,2d'filename' (3认同)
  • 适用于 Ubuntu (GNU),但对于 OS X (BSD),我必须将其更改为 `sed -i '' '1d' filename`。根据 /sf/ask/1172219191/ (3认同)

Nas*_*jib 71

对于那些使用非GNU的SunOS的人,以下代码将有所帮助:

sed '1d' test.dat > tmp.dat 
Run Code Online (Sandbox Code Playgroud)

  • 有趣的人口统计 (18认同)

pax*_*blo 18

不,那就像你要获得的那样高效.您可以编写一个C程序,它可以更快地完成工作(减少启动时间和处理参数)但它可能会趋向于与文件变大的sed相同的速度(并且我认为如果它需要一分钟就会很大).

但是你的问题与许多其他问题一样,因为它预先设定了解决方案.如果你要详细告诉我们什么你想要做而不是如何,我们也许能够提出更好的选择.

例如,如果这是某个其他程序B处理的文件A,则一种解决方案是不剥离第一行,而是修改程序B以不同方式处理它.

假设所有程序都附加到此文件A,程序B当前在删除它之前读取并处理第一行.

您可以重新设计程序B,以便它不会尝试删除第一行但是在文件A中保持一个持久的(可能是基于文件的)偏移量,以便下次运行时可以寻找该偏移量,进程那条线,并更新偏移量.

然后,在安静的时间(午夜?),它可以对文件A进行特殊处理,以删除当前处理的所有行,并将偏移量设置回0.

程序打开和查找文件而不是打开和重写肯定会更快.当然,本讨论假定您可以控制程序B. 我不知道是否是这种情况,但如果您提供进一步的信息,可能还有其他可能的解决方案.

  • @db 在这种情况下,`awk FNR-1 *.csv` 可能更快。 (4认同)

ale*_*xis 10

可以编辑文件:只需使用perl的-i标志,如下所示:

perl -ni -e 'print unless $. == 1' filename.txt
Run Code Online (Sandbox Code Playgroud)

这会使第一行消失,正如你所问的那样.Perl需要读取和复制整个文件,但它会安排输出以原始文件的名称保存.


Rob*_*ble 9

正如Pax所说,你可能不会比这更快.原因是几乎没有文件系统支持从文件开头截断,因此这将是一个O(n)操作,其中n是文件的大小.你可以做,虽然速度是覆盖具有相同的字节数(也许用空格或注释),这可能会为您取决于正是你正在尝试做的工作第一线(那是什么来着?).

  • @agc:现在无关紧要,但我在 70 年代的第一份工作是在 Quadex,一家小型初创公司(现已消失,与现在使用该名称的两家公司无关)。他们有一个文件系统,允许在文件的开头或结尾添加或删除,主要用于通过在文件中放置窗口上方和窗口下方来实现小于 3KB 的编辑。它没有自己的名称,只是 QMOS(Quadex 多用户操作系统)的一部分。(在 RAM 低于 64KB 的 LSI-11/02 上,“Multi”通常为 2-3 个,通常还有一些 RX01 型 8" 软盘,每个 250KB。):-) (4认同)

agc*_*agc 8

spongeUTIL避免了杂耍一个临时文件的需要:

tail -n +2 "$FILE" | sponge "$FILE"
Run Code Online (Sandbox Code Playgroud)

  • 应该明确的是,“sponge”需要安装“moreutils”包。 (3认同)
  • `sponge` 确实比公认的解决方案更干净、更健壮 (`tail -n +2 "$FILE" > "$FILE.tmp" && mv "$FILE.tmp" "$FILE"`) (2认同)
  • 但是“sponge”是否将整个文件缓冲在内存中?如果它是数百GB,那将不起作用。 (2认同)
  • @OrangeDog,只要文件系统可以存储它,`sponge`就会吸收它,因为它使用 */tmp* 文件作为中间步骤,然后用它来替换原来的文件。 (2认同)

Mar*_*eed 8

如果要修改到位的文件,你总是可以使用原始ed的,而不是它的小号 treaming继任者sed

ed "$FILE" <<<$'1d\nwq\n'
Run Code Online (Sandbox Code Playgroud)

ed命令是原始的UNIX文本编辑器,甚至没有全屏终端,而图形工作站则少得多。在ex编辑器中,最有名的你使用的是什么类型时,在结肠中的提示vi,是一个的趋向版本ed,所以很多相同的命令工作。尽管ed本意是要交互使用,但也可以通过向其发送一串命令来以批处理方式使用它,这就是该解决方案的作用。

序列<<<$'1d\nwq\n'利用了bash的支持,这里串(<<<)和POSIX引号($'... '),以饲料投入到ed由两行命令:1d,其中d eletes行1,然后wq,这W¯¯仪式的文件重新出磁盘,然后q UITS编辑会话。


ser*_*rup 6

应该显示除第一行之外的行:

cat textfile.txt | tail -n +2
Run Code Online (Sandbox Code Playgroud)

  • @niglesiais 我不同意“猫的无用使用”,因为它清楚地表明此解决方案适用于管道内容,而不仅仅是文件。 (5认同)
  • - 你应该做“tail -n +2 textfile.txt” (4认同)

小智 6

您可以轻松地做到这一点:

cat filename | sed 1d > filename_without_first_line
Run Code Online (Sandbox Code Playgroud)

在命令行上;或要永久删除文件的第一行,请使用sed的就地模式和以下-i标志:

sed -i 1d <filename>
Run Code Online (Sandbox Code Playgroud)

  • 从技术上讲,“-i”选项采用一个参数,指定在备份文件时要使用的文件后缀(例如“sed -I .bak 1d filename”创建原始文件的一个名为“filename.bak”的副本,其中第一个线路完好)。虽然 GNU sed 允许您指定不带参数的“-i”来跳过备份,但 BSD sed(如 macOS 上所示)需要一个空字符串参数作为单独的 shell 单词(例如“sed -i '' ...”)。 (3认同)

小智 5

使用csplit怎么样?

man csplit
csplit -k file 1 '{1}'
Run Code Online (Sandbox Code Playgroud)


Hon*_*Liu 5

可以使用vim来做到这一点:

vim -u NONE +'1d' +'wq!' /tmp/test.txt
Run Code Online (Sandbox Code Playgroud)

这应该更快,因为vim在处理时不会读取整个文件。