我需要使用bash脚本从巨大的文本文件中重复删除第一行。
现在我正在使用sed -i -e "1d" $FILE
- 但删除大约需要一分钟。
有没有更有效的方法来实现这一目标?
答案 0 :(得分:901)
尝试tail:
tail -n +2 "$FILE"
-n x
:只需打印最后x
行。 tail -n 5
将为您提供输入的最后5行。 +
符号会反转参数,并使tail
打印除第一行x-1
之外的任何内容。 tail -n +1
将打印整个文件,tail -n +2
除了第一行之外的所有内容,等等。
GNU tail
比sed
快得多。 BSD上也提供tail
,-n +2
标记在两个工具中都是一致的。查看FreeBSD或OS X手册页了解更多信息。
但是,BSD版本可能比sed
慢得多。我想知道他们是如何做到的; tail
应该逐行读取文件,而sed
执行相当复杂的操作,包括解释脚本,应用正则表达式等。
注意:您可能想要使用
# THIS WILL GIVE YOU AN EMPTY FILE!
tail -n +2 "$FILE" > "$FILE"
但这会给你一个空文件。原因是重定向(>
)发生在shell调用tail
之前:
$FILE
tail
tail
进程的标准输出重定向到$FILE
tail
从现在为空的$FILE
如果要删除文件中的第一行,则应使用:
tail -n +2 "$FILE" > "$FILE.tmp" && mv "$FILE.tmp" "$FILE"
&&
将确保在出现问题时不会覆盖该文件。
答案 1 :(得分:131)
您可以使用-i更新文件,而无需使用'>'运营商。以下命令将从文件中删除第一行并将其保存到文件中。
sed -i '1d' filename
答案 2 :(得分:69)
对于那些使用非GNU的SunOS的人,以下代码将有所帮助:
sed '1d' test.dat > tmp.dat
答案 3 :(得分:17)
不,这就像你要获得的那样高效。您可以编写一个C程序,它可以更快地完成工作(减少启动时间和处理参数)但它可能会趋向于与文件变大的sed相同的速度(并且我认为如果它需要一分钟就会很大)。
但你的问题与许多其他问题一样,因为它预先设定了解决方案。如果你要详细告诉我们你想要做什么而不是如何,我们可能会建议更好的选择。
例如,如果这是某个其他程序B处理的文件A,则一种解决方案是不剥离第一行,而是修改程序B以不同方式处理它。
假设所有程序都附加到此文件A,程序B当前会在删除之前读取并处理第一行。
你可以重新设计程序B,这样它就不会尝试删除第一行,而是在文件A中保持一个持久的(可能是基于文件的)偏移量,以便下次运行时它可以寻找offset,处理那里的行,并更新偏移量。
然后,在安静的时间(午夜?),它可以对文件A进行特殊处理,以删除当前处理的所有行,并将偏移量设置回0。
程序打开和查找文件肯定会更快,而不是打开和重写。当然,本讨论假定您可以控制程序B.我不知道是否是这种情况,但如果您提供进一步的信息,可能还有其他可能的解决方案。
答案 4 :(得分:11)
您可以编辑文件:只需使用perl的-i
标记,如下所示:
perl -ni -e 'print unless $. == 1' filename.txt
这会让你的第一行消失。 Perl需要读取和复制整个文件,但它会安排输出以原始文件的名称保存。
答案 5 :(得分:9)
正如Pax所说,你可能不会比这更快。原因是几乎没有文件系统支持从文件开头截断,因此这将是一个O(n
)操作,其中n
是文件的大小。你可以做得更快更多虽然用相同的字节数(可能带有空格或注释)覆盖第一行,这可能对你有用,具体取决于你想要做什么(什么是顺便说一下?)。
答案 6 :(得分:8)
如果您要修改文件,可以始终使用原始ed
代替 s treaming successor sed
:
ed "$FILE" <<<$'1d\nwq\n'
ed
命令是原始的UNIX文本编辑器,甚至还有全屏终端,更不用说图形工作站了。 ex
编辑器,最常见的是您在vi
中的冒号提示符处输入时所使用的编辑器,是 ex 缩放版ed
,所以很多相同的命令工作。虽然ed
旨在以交互方式使用,但它也可以通过向其发送一串命令来以批处理模式使用,这就是此解决方案的作用。
序列<<<$'1d\nwq\n'
利用Bash对here-strings(<<<
)和POSIX引号($'
... '
)的支持,将输入提供给{ {1}}命令由两行组成:ed
, d eletes 1 ,然后1d
, w < / strong>将文件返回磁盘,然后 q 进行编辑会话。
答案 7 :(得分:7)
sponge
util避免了处理临时文件的需要:
tail -n +2 "$FILE" | sponge "$FILE"
答案 8 :(得分:5)
可以使用vim执行此操作:
vim -u NONE +'1d' +'wq!' /tmp/test.txt
这应该更快,因为vim在处理时不会读取整个文件。
答案 9 :(得分:4)
如何使用csplit?
man csplit
csplit -k file 1 '{1}'
答案 10 :(得分:4)
您可以轻松地执行以下操作:
cat filename | sed 1d > filename_without_first_line
在命令行上;或永久删除文件的第一行,请使用带有-i
标志的sed的就地模式:
sed -i 1d <filename>
答案 11 :(得分:3)
应显示除第一行以外的行:
cat textfile.txt | tail -n +2
答案 12 :(得分:1)
由于听起来我无法加速删除,我认为一个好的方法可能是批处理这样的文件:
While file1 not empty
file2 = head -n1000 file1
process file2
sed -i -e "1000d" file1
end
这样做的缺点是,如果程序在中间被杀死(或者如果那里有一些不好的sql - 导致“进程”部分死亡或锁定),则会有跳过的行,或处理两次。
(file1包含sql代码行)
答案 13 :(得分:0)
如果你想要做的就是在失败后恢复,你可以建立一个到目前为止已完成的文件。
if [[ -f $tmpf ]] ; then
rm -f $tmpf
fi
cat $srcf |
while read line ; do
# process line
echo "$line" >> $tmpf
done
答案 14 :(得分:0)
这名班轮将:
echo "$(tail -n +2 "$FILE")" > "$FILE"
它起作用,因为tail
在echo
之前执行,然后文件被解锁,因此不需要临时文件。
答案 15 :(得分:-1)
在N-1行上使用tail并将其指向文件,然后删除旧文件,并将新文件重命名为旧名称吗?
如果我以编程方式执行此操作,我将读取文件,并在读取每一行后记住文件偏移量,这样我就可以回到该位置以读取文件中少一行。