我需要使用bash脚本从一个巨大的文本文件中重复删除第一行。
现在我正在使用sed -i -e"1d" $FILE - 但删除大约需要一分钟。
有没有更有效的方法来实现这一目标?
-
-i代表什么?
-
@cikatomo:它代表内联编辑 - 它用您生成的任何内容编辑文件。
-
尾巴比sed大很多。 尾巴需要13.5s,sed需要0.85s。 我的文件有大约1M行,大约100MB。 带SSD的MacBook Air 2013。
试试尾巴:
-n x:只需打印最后的x行。 tail -n 5将为您提供输入的最后5行。 +符号类型反转参数并使tail打印除第一个x-1行之外的任何内容。 tail -n +1将打印整个文件,tail -n +2除第一行外的所有内容,等等。
GNU tail比sed快得多。 tail也可在BSD上使用,-n +2标志在两个工具中都是一致的。查看FreeBSD或OS X手册页了解更多信息。
但是,BSD版本可能比sed慢得多。我想知道他们是如何做到的; tail应该只是逐行读取文件,而sed执行相当复杂的操作,包括解释脚本,应用正则表达式等。
注意:您可能很想使用
1 2
| # THIS WILL GIVE YOU AN EMPTY FILE!
tail -n +2"$FILE">"$FILE" |
但这会给你一个空文件。原因是重定向(>)发生在shell调用tail之前:
Shell截断文件$FILE
Shell为tail创建了一个新进程
Shell将tail进程的stdout重定向到$FILE
tail从现在为空的$FILE读取
如果要删除文件中的第一行,则应使用:
1
| tail -n +2"$FILE">"$FILE.tmp" && mv"$FILE.tmp""$FILE" |
&&将确保在出现问题时不会覆盖该文件。
-
它不适用于15Mb或更多的行
-
@user:有趣的一点。你从哪里得到这个号码?
-
根据这个ss64.com/bash/tail.html,当使用BSD'tail'和-r选项时,典型的缓冲区默认为32k。也许系统中某处有缓冲区设置?或-n是32位有符号数?
-
嗯,刚刚为我工作的92 M文件删除了第一个400k +线。
-
@Eddie:user869097表示当一行为15Mb或更多时,它不起作用。只要行更短,tail将适用于任何文件大小。
-
哎呀。谢谢你纠正我。 WO,15mb线..我甚至无法想象这样的情况。
-
你能解释一下这些论点吗?
-
@Eddie我有时会在程序上看到它们,这些程序将整个数据库作为XML输出,而不是在关键位置插入换行符。
-
@Dreampuf:sed有一个当前行的内部缓冲区,而tail可以通过记住N个最后一个换行符的偏移而离开(注意我实际上没有查看源代码)。
-
将输出写入文件更好:tail -n +2"$FILE"> newfile
-
@Dreampuf - 来自手册页:-n N means output the last N lines, instead of the last 10; or use +N to output lines starting with the Nth
-
在这种情况下,为什么尾巴比sed快?
-
@CMCDragonkai Tail是专门用于此任务的工具。 Sed是一种通用工具。它将创建一个内部数据结构,将操作应用于每一行(1d只匹配第一行,但我不确定sed是否优化了这种情况,例如)。
-
尾巴比sed大很多。尾巴需要13.5s,sed需要0.85s。我的文件有大约1M行,大约100MB。带SSD的MacBook Air 2013。
-
@JonaChristopherSahnwaldt Intersting。您是否多次运行以排除缓存等?
-
@AaronDigulla我跑了两次。我可以将结果粘贴到聊天窗口中。这里不知道怎么做...
-
@JonaChristopherSahnwaldt和结果文件是一样的吗?我不确定你的SSD有多快,但是读取和写入100MB的文件应该已经花了大约1秒钟。
-
@AaronDigulla是的,他们是一样的。
-
@AaronDigulla docs.google.com/document/d/…
-
@JonaChristopherSahnwaldt我对这些数字非常非常惊讶。这就像Windows Word打印速度快于echo | lpr。我没有时间调试tail,所以我不知道为什么它在你的情况下更慢。我的直觉是这是长队但我不知道。
-
@AaronDigulla线条不长。平均100个字节。
-
@AaronDigulla:机器上的sed / tail有多快或多慢?
-
@JonaChristopherSahnwaldt在我的电脑上(Windows 8,Cygwin,sed 4.2.2,tail 8.24)。 100MB文本,短行(<80个字符)。 time cat sample.txt > /dev/null需要0.06秒(仅缓存中的IO)。 time sed -e"1d" sample.txt > /dev/null需要1.12s,time tail -n +2 sample.txt > /dev/null需要0.22s。 sed比tail慢大约6倍。
-
我打算同意@JonaChristopherSahnwaldt - 尾巴比sed变体要快得多,比一个数量级要慢得多。我正在一个500,000K行的文件上测试它(每行不超过50个字符)。然而,我意识到我使用的是FreeBSD版本的tail(默认情况下附带OS X)。当我切换到GNU尾部时,尾部调用比sed调用快10倍(以及GNU sed调用)。如果你正在使用GNU,那么AaronDigulla在这里是正确的。
-
谢谢。我已经编辑了我的答案,强调这一点。
-
关于sed的好处是,您可以使用它来编辑文件,这是tail所不能做到的(据我所知。如果我错了请纠正我)。如果要删除目录中所有文件的第一行,可以执行类似sed -i"1d" *的操作。我猜你也可以通过与find结合使用或制作脚本来自动化tail,但我不确定哪一个表现更好。我知道OP提到他们正在使用-i,但我认为这可能有助于澄清其用途。
-
有没有办法在多个文件上使用这样的尾部?我有几个文件,1.txt,2.txt等我想要执行此操作,我希望输出最终在1.txt,2.txt或1.fixed,2.fixed或类似的东西。
-
@ d-b否。使用循环。
您可以使用-i更新文件,而无需使用">"运算符。以下命令将从文件中删除第一行并将其保存到文件中。
-
我收到错误:unterminated transform source string
-
当我定时操作时,sed要快得多。
-
这每次都有效,真的应该是最好的答案!
-
请记住,当使用带有就地编辑的sed时,Mac需要提供后缀。所以用-i.bak运行上面的代码
-
只需注意 - 删除几行使用sed -i '1,2d' filename
-
这个版本比tail -n +2更具可读性,更通用。不确定为什么它不是最佳答案。
-
除了(GNU)tail与sed相比显着的时间减少之外,应该注意的是,尽管-i选项,sed仍然需要创建文件的副本,所以这个解决方案不会是面对有限的磁盘空间问题时,比tail更有帮助。
-
@LukeDavis,因为问题是要求比这更快的东西。
对于那些使用非GNU的SunOS的人,以下代码将有所帮助:
1
| sed '1d' test.dat > tmp.dat |
-
有趣的人口统计
-
我更喜欢这个版本因为它读得更好。 :)
不,那就像你要获得的那样高效。您可以编写一个C程序,它可以更快地完成工作(减少启动时间和处理参数)但它可能会趋向于与文件变大的sed相同的速度(并且我认为如果它需要一分钟就会很大)。
但是你的问题与许多其他问题一样,因为它预先设定了解决方案。如果你要详细告诉我们你要做什么而不是如何做,我们可能会建议一个更好的选择。
例如,如果这是某个其他程序B处理的文件A,则一种解决方案是不剥离第一行,而是修改程序B以不同方式处理它。
假设所有程序都附加到此文件A,程序B当前在删除它之前读取并处理第一行。
您可以重新设计程序B,这样它就不会尝试删除第一行,而是在文件A中保持一个持久的(可能是基于文件的)偏移量,以便下次运行时可以寻找该偏移量,进程那条线,并更新偏移量。
然后,在安静的时间(午夜?),它可以对文件A进行特殊处理,以删除当前处理的所有行,并将偏移量设置回0。
程序打开和查找文件而不是打开和重写肯定会更快。当然,本讨论假定您可以控制程序B.我不知道是否是这种情况,但如果您提供进一步的信息,可能还有其他可能的解决方案。
-
也称为X-Y问题。
-
我认为OP正在努力实现让我找到这个问题的原因。我有10个CSV文件,每个文件有500k行。每个文件都与第一行具有相同的标题行。我是猫:将这些文件放入一个文件,然后将它们导入到DB中,让DB从第一行创建列名。显然我不希望在文件2-10中重复该行。
-
@ d-b在这种情况下,awk FNR-1 *.csv可能更快。
您可以编辑文件:只需使用perl的-i标志,如下所示:
1
| perl -ni -e 'print unless $. == 1' filename.txt |
这会使第一行消失,正如你所问的那样。 Perl需要读取和复制整个文件,但它会安排输出以原始文件的名称保存。
正如Pax所说,你可能不会比这更快。原因是几乎没有文件系统支持从文件开头截断,因此这将是一个O(n)操作,其中n是文件的大小。你可以做得更快但是用相同的字节数(可能带有空格或注释)覆盖第一行,这可能对你有用,具体取决于你想要做什么(顺便说一句是什么?)。
-
重新"......几乎没有支持截断的文件系统......":这很有趣;请考虑包括命名此类文件系统的括号注释。
如果要修改文件,可以始终使用原始ed而不是其流式继承器sed:
1 2 3
| ed"$FILE" <<<$'1d
wq
' |
ed命令是原始的UNIX文本编辑器,甚至还有全屏终端,更不用说图形工作站了。 ex编辑器,最着名的是在vi中的冒号提示符下键入时所使用的编辑器,是ed的扩展版本,因此许多相同的命令都有效。虽然ed旨在以交互方式使用,但它也可以通过向其发送一串命令来以批处理模式使用,这就是此解决方案的作用。
序列<<<$'1d
wq
'利用Bash对here-strings(<<<)和POSIX引号($' ... ')的支持,将输入提供给由两行组成的ed命令:1d ,删除第1行,然后删除wq,将文件写回磁盘,然后退出编辑会话。
-
这很优雅。+1
-
但是你必须将整个文件读入内存,如果数百GB则无法使用。
sponge util避免了处理临时文件的需要:
1
| tail -n +2"$FILE" | sponge"$FILE" |
-
sponge确实比公认的解决方案更清晰,更强大(tail -n +2"$FILE">"$FILE.tmp" && mv"$FILE.tmp""$FILE")
-
应该明确"海绵"需要安装'moreutils'包。
-
这是我唯一能够更改系统文件的解决方案(在Debian docker镜像上)。尝试写入文件时,由于"设备或资源忙"错误导致其他解决方案失败。
-
但sponge是否将整个文件缓冲在内存中?如果数百GB,这将无法工作。
-
@OrangeDog,只要文件系统可以存储它,sponge就会将它浸泡,因为它使用/ tmp文件作为中间步骤,然后用它来替换原始文件。
可以使用vim执行此操作:
1
| vim -u NONE +'1d' +'wq!' /tmp/test.txt |
这应该更快,因为vim在处理时不会读取整个文件。
-
如果你的shell是bash,可能需要引用+wq!。 可能不是因为!不是一个单词的开头,但养成引用东西的习惯可能是好的。 (如果你不通过不必要的引用来提高效率,你也不需要1d周围的引号。)
用csplit怎么样?
1 2
| man csplit
csplit -k file 1 '{1}' |
-
此语法也可以,但只生成两个输出文件而不是三个:csplit file /^.*$/1。或者更简单:csplit file //1。或者更简单:csplit file 2。
您可以轻松地执行此操作:
1
| cat filename | sed 1d > filename_without_first_line |
在命令行上;或者永久删除文件的第一行,使用带有-i标志的sed的就地模式:
应显示除第一行以外的行:
1
| cat textfile.txt | tail -n +2 |
-
- 你应该做"tail -n +2 textfile.txt"
-
@niglesiais我不同意"无用的猫",因为它清楚地表明这个解决方案对于管道内容而言不仅仅是文件。
由于听起来我无法加速删除,我认为一个好方法可能是批量处理文件,如下所示:
1 2 3 4 5
| While file1 not empty
file2 = head -n1000 file1
process file2
sed -i -e"1000d" file1
end |
这样做的缺点是,如果程序在中间被杀死(或者如果那里有一些不好的sql - 导致"进程"部分死亡或锁定),则会有跳过或被处理两次的行。
(file1包含sql代码行)
-
第一行包含什么?你可以用我在帖子中建议的sql注释覆盖它吗?
如果你想要做的是在失败后恢复,你可以建立一个具有你迄今为止所做的事情的文件。
1 2 3 4 5 6 7 8
| if [[ -f $tmpf ]] ; then
rm -f $tmpf
fi
cat $srcf |
while read line ; do
# process line
echo"$line">> $tmpf
done |
会在N-1行上使用tail并将其导入文件,然后删除旧文件,并将新文件重命名为旧名称吗?
如果我以编程方式执行此操作,我会读取文件,并在读取每一行后记住文件偏移量,因此我可以回到该位置以读取文件中少一行。
-
第一个解决方案与布伦特现在正在做的基本相同。我不理解您的编程方法,只需要删除第一行,您只需读取并丢弃第一行并将其余行复制到另一个文件,该文件与sed和tail方法相同。
-
第二种解决方案的含义是文件不会每次都被第一行收缩。程序只是处理它,好像它已经缩小了,但每次从下一行开始
-
我仍然不明白你的第二个解决方案是什么。