【问题标题】:Delete third-to-last line of file using sed or awk使用 sed 或 awk 删除文件的倒数第三行
【发布时间】:2020-09-25 20:40:16
【问题描述】:

我有几个具有不同行号的文本文件,我必须在所有这些文件中删除倒数第三行。这是一个示例文件:

bear
horse
window
potato
berry
cup

此文件的预期结果:

bear
horse
window
berry
cup

我们可以删除文件的倒数第三行吗:
一个。不基于任何字符串/模式。
湾。仅基于它必须是倒数第三行的条件

我对如何从最后一行开始索引我的文件有疑问。我已经从倒数第二行的另一个 SO 问题中尝试了这个:

> sed -i 'N;$!P;D' output1.txt

【问题讨论】:

  • sed 对于s/old/new/ 以外的任何东西都是错误的工具。如果您使用除 s、g 和 p(带 -n)之外的任何 sed 结构,那么您应该改用 awk。

标签: awk sed ed


【解决方案1】:

使用tac + awk 解决方案,请您尝试关注。只需将awkline 变量设置为行(从底部开始),无论您想跳过哪个。

tac Input_file | awk -v line="3" 'line==FNR{next} 1' | tac

解释: 使用tac 将反向读取Input_file(从底行到第一行),将其输出传递给awk 命令,然后检查条件是否line 等于 line(我们要跳过)然后不打印该行,1 将打印其他行。

第二个解决方案:使用awk + wc 解决方案,请尝试关注。

awk -v lines="$(wc -l < Input_file)" -v skipLine="3" 'FNR!=(lines-skipLine+1)' Input_file

解释:在这里启动awk 程序并创建一个变量lines,其中包含Input_file 中存在的总行数。变量skipLine 具有我们想要从 Input_file 底部跳过的行号。然后在主程序检查条件下,如果当前行不等于lines-skipLine+1,则打印这些行。

第三种解决方案:在此处根据 Ed sir 的评论添加解决方案。

awk -v line=3 '{a[NR]=$0} END{for (i=1;i<=NR;i++) if (i != (NR-line)) print a[i]}' Input_file

说明:为第 3 个解决方案添加详细说明。

awk -v line=3 '             ##Starting awk program from here, setting awk variable line to 3(line which OP wants to skip from bottom)
{
  a[NR]=$0                  ##Creating array a with index of NR and value is current line.
}
END{                        ##Starting END block of this program from here.
  for(i=1;i<=NR;i++){       ##Starting for loop till value of NR here.
    if(i != (NR-line)){     ##Checking condition if i is NOT equal to NR-line then do following.
      print a[i]            ##Printing a with index i here.
    }
  }
}
' Input_file                ##Mentioning Input_file name here.

【讨论】:

  • 并且很可能使用 awk 变量是矫枉过正的,OP 会很高兴NR!=3'
【解决方案2】:

ed

ed -s ip.txt <<< $'$-2d\nw'

# thanks Shawn for a more portable solution
printf '%s\n' '$-2d' w | ed -s ip.txt

这将进行就地编辑。 $ 指最后一行,您可以指定一个负的相对值。因此,$-2 将引用最后但第二行。然后w 命令将写入更改。

更多详情请见ed: Line addressing

【讨论】:

  • 我喜欢看到除了我之外的人在答案中使用ed。它再次成为一件事! (虽然我会使用 printf '%s\n' '$-2d' w | ed -s ip.txt 而不是依赖 bashisms)
  • @Shawn 所有这些帖子都给我留下了深刻的印象,我认为这是我第一次尝试这样的答案。我可能会尝试更多地了解它并写一篇博文
【解决方案3】:

这可能对你有用(GNU sed):

sed '1N;N;$!P;D' file

在文件中打开一个包含 3 行的窗口,然后打印/删除窗口的第一行,直到文件结束。

在文件末尾,不要打印窗口中的第一行,即从文件末尾算起的第三行。相反,删除它,然后重复 sed 循环。这将尝试在文件末尾附加一行,这将导致 sed 退出,在窗口中打印剩余的行。

向后 n 行的通用解决方案(其中 n 是文件末尾的 2 行或更多行)是:

sed ':a;N:s/[^\n]*/&/3;Ta;$!P;D' file 

当然你可以使用:

tac file | sed 3d | tac

但是你会读取文件 3 次。

【讨论】:

  • 对于大输入,tac | sed '3d' | tac 会比sed '1N;N;$!P;D' file 快(已测试),而 head 和 tail 的命令组(见:stackoverflow.com/a/64049387/7589636)会快几倍,因为它没有' t 真正阅读这些行,但 seek() 用于行号并转储内容。与此类似,taccat 类似,表示快速,sed '3d' 也紧随其后,但对于所有输入,sed 折叠行将再次读取和写入相同的行。
【解决方案4】:

要删除文件的倒数第三行,可以使用headtail

{ head -n -3 file; tail -2 file; }

如果输入文件很大,当性能很重要时,这是非常快的,因为它不会逐行读取和写入。另外,不要修改括号旁边的分号和空格,请参阅commands grouping


或将sedtac 一起使用:

tac file | sed '3d' | tac

或将awktac 一起使用:

tac file | awk 'NR!=3' | tac

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2023-03-05
    • 1970-01-01
    • 1970-01-01
    • 2016-02-04
    • 2013-10-08
    • 2013-02-27
    • 2023-01-26
    • 2013-11-17
    相关资源
    最近更新 更多