繁体   English   中英

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

[英]How can I remove the first line of a text file using bash/sed script?

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

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

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

试试尾巴

tail -n +2 "$FILE"

-nx :只打印最后x行。 tail -n 5会给你输入的最后 5 行。 +号会反转参数并使tail打印除前x-1行之外的任何内容。 tail -n +1将打印整个文件, tail -n +2除了第一行之外的所有内容,等等。

GNU tailsed快得多。 tail在 BSD 上也可用,并且-n +2标志在两个工具中是一致的。 查看FreeBSDOS X手册页以获取更多信息。

但是,BSD 版本可能比sed慢得多。 我想知道他们是如何做到的; tail应该只逐行读取文件,而sed执行非常复杂的操作,包括解释脚本、应用正则表达式等。

注意:您可能很想使用

# THIS WILL GIVE YOU AN EMPTY FILE!
tail -n +2 "$FILE" > "$FILE"

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

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

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

tail -n +2 "$FILE" > "$FILE.tmp" && mv "$FILE.tmp" "$FILE"

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

您可以使用 -i 来更新文件,而无需使用 '>' 运算符。 以下命令将从文件中删除第一行并将其保存到文件中(在后台使用临时文件)。

sed -i '1d' filename

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

sed '1d' test.dat > tmp.dat 

您可以通过以下方式轻松做到这一点:

cat filename | sed 1d > filename_without_first_line

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

sed -i 1d <filename>

不,这与您将获得的效率差不多。 您可以编写一个 C 程序,它可以更快地完成这项工作(更少的启动时间和处理参数),但随着文件变大,它可能会趋向与 sed 相同的速度(如果需要一分钟,我假设它们很大) )。

但是您的问题与许多其他问题存在相同的问题,因为它预先假定了解决方案。 如果您要详细告诉我们您要做什么而不是如何做,我们可能会提出更好的选择。

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

假设您的所有程序都附加到此文件 A 和程序 B 当前读取并处理第一行,然后再删除它。

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

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

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

sponge实用程序避免了处理临时文件的需要:

tail -n +2 "$FILE" | sponge "$FILE"

如果你想修改文件,你总是可以使用原始的ed而不是它的 s处理后继者sed

ed "$FILE" <<<$'1d\nwq\n'

ed命令是最初的 UNIX 文本编辑器,甚至还没有全屏终端,更不用说图形工作站了。 ex编辑器,最广为人知的是您在vi中在冒号提示符下键入时使用的编辑器,它是ed扩展版本,因此许多相同的命令都可以工作。 虽然ed旨在以交互方式使用,但它也可以通过向其发送一串命令以批处理模式使用,这就是该解决方案的作用。

序列<<<$'1d\nwq\n'利用现代 shell 对 here-strings ( <<< ) 和 ANSI 引号 ( $' ... ' ) 的支持将输入提供给由两个组成的ed命令行: 1d ,删除1行,然后wq ,将文件写回磁盘,然后退出编辑会话。

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

perl -ni -e 'print unless $. == 1' filename.txt

如您所问,这会使第一行消失。 Perl 将需要读取和复制整个文件,但它会安排将输出保存在原始文件的名称下。

正如 Pax 所说,您可能不会比这更快。 原因是几乎没有文件系统支持从文件开头截断,所以这将是一个 O( n ) 操作,其中n是文件的大小。 您可以更快地做的是用相同数量的字节(可能带有空格或注释)覆盖第一行,这可能对您有用,具体取决于您正在尝试做什么(顺便说一句?)。

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

cat textfile.txt | tail -n +2

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

vim -u NONE +'1d' +'wq!' /tmp/test.txt

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

使用 csplit 怎么样?

man csplit
csplit -k file 1 '{1}'

这一个班轮将做:

echo "$(tail -n +2 "$FILE")" > "$FILE"

它可以工作,因为tailecho之前执行,然后文件被解锁,因此不需要临时文件。

由于听起来我无法加快删除速度,我认为一个好的方法可能是像这样批量处理文件:

While file1 not empty
  file2 = head -n1000 file1
  process file2
  sed -i -e "1000d" file1
end

这样做的缺点是,如果程序在中间被杀死(或者如果那里有一些错误的 sql - 导致“进程”部分死亡或锁定),则会有一些行被跳过或处理两次.

(file1 包含几行 sql 代码)

tail +2 path/to/your/file

对我有用,无需指定-n标志。 原因请参见Aaron 的回答

如果您要做的是在失败后恢复,您可以构建一个包含您迄今为止所做的文件的文件。

if [[ -f $tmpf ]] ; then
    rm -f $tmpf
fi
cat $srcf |
    while read line ; do
        # process line
        echo "$line" >> $tmpf
    done

基于其他 3 个答案,我想出了在我的 Mac OSx bash shell 中完美运行的语法:

line=$(head -n1 list.txt && echo "$(tail -n +2 list.txt)" > list.txt)

测试用例:

~> printf "Line #%2d\n" {1..3} > list.txt
~> cat list.txt
Line # 1
Line # 2
Line # 3
~> line=$(head -n1 list.txt && echo "$(tail -n +2 list.txt)" > list.txt)
~> echo $line
Line # 1
~> cat list.txt
Line # 2
Line # 3

您可以使用sed命令按行号删除任意行

# create multi line txt file
echo """1. first
2. second
3. third""" > file.txt

删除行并打印到标准输出

$ sed '1d' file.txt 
2. second
3. third

$ sed '2d' file.txt 
1. first
3. third

$ sed '3d' file.txt 
1. first
2. second

# delete multi lines
$ sed '1,2d' file.txt 
3. third

# delete the last line
sed '$d' file.txt 
1. first
2. second

使用-i选项就地编辑文件

$ cat file.txt 
1. first
2. second
3. third

$ sed -i '1d' file.txt

$cat file.txt 
2. second
3. third

是否会在 N-1 行上使用 tail 并将其定向到文件中,然后删除旧文件,并将新文件重命名为旧名称来完成这项工作?

如果我以编程方式执行此操作,我将在读取每一行之后通读文件并记住文件偏移量,因此我可以回到该位置以读取其中少一行的文件。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM