删除第n行(从底部算起)

删除第n行(从底部算起)

文件中的行数未知。如何在Unix平台上用一行命令(必要时可以使用多行)删除第n行(从下往上算)。

答案1

例如,要使用以下命令删除底部的第四行sed

tac input | sed '4d' | tac

要覆盖输入文件:

tmpfile=$(mktemp)
tac input | sed '4d' | tac > "$tmpfile" && mv "$tmpfile" input

答案2

纯的sed

  • 如果n是 1:

    sed '$ d'
    

    这很简单:如果是最后一行,则删除模式空间,这样就不会打印它。

  • 如果n大于 1(可用作$n):

    sed "
       : start
       1,$((n-1)) { N; b start }
       $ { t end; s/^//; D }
       N
       P
       D
       : end
     "
    

    注意在启动$((n-1))之前由 shell 展开sed

    这个片段

    : start
    1,$((n-1)) { N; b start }
    

    商店n模式空间中的 -1 行。如果sed在此循环中到达输入流的末尾,则将自动打印模式空间(从末尾开始没有第 n 行,不会删除任何行)。

    假设有更多的输入。然后,在到达最后一行之前,迭代此片段:

    N   # read the next line of input and append it to the pattern space
    P   # print the first line from the pattern space
    D   # delete the first line from the pattern space and start a new cycle
    

    这样,模式空间就是我们的缓冲区,它使输出根据输入“延迟”几行。N从这个片段中也可以读取输入的最后一行。

    读取最后一行后,将执行:

    $ { t end; s/^//; D }
    

    当第一次执行此代码时,t不会分支到,end因为之前没有成功替换。s/^//然后执行这种无操作替换,并且模式空间中的第一行被删除 ( D) 而不被打印。这正是您要删除的行。由于D开始了新的循环,同一行代码最终会再次被执行。这次t将分支到end.

    sed到达脚本末尾时,将自动打印模式空间。这样所有剩余的行都会被打印出来。

    该命令将为n=2(有效)和n=1(无效)生成相同的输出。我试图找到一个无论什么情况都有效的单一解决方案n。我失败了,因此当你的特殊情况n是 1。

答案3

如果$n保留要删除的行数

删除单行使用

printf "\$-%d+1,\$-%d+1d\nwq\n" $n $n| ed -s file

删除最后 n 行

printf "\$-%d,\$d\nwq\n" $n | ed -s file

在哪里

  • \$%d,\$d告诉 ed 删除最后 n 行(printf 将插入 n)
  • wq写完然后退出
  • -sined -s会让ed保持沉默。

  • 请注意,没有规定检查是否有足够的行可以删除。

可悲的是范围从结束不能在sed...中指定

答案4

您可以结合headtail来实现这一点。

如果第n个需要删除底部的一行

  1. head从标准输入读取并向标准输出报告总-n从顶部开始的线条
  2. tail读取并报告到底部的标准输出n-1来自标准输入的行。
  3. 作为一个整体,这些行将按顺序报告到标准输出,并跳过从末尾开始的 *nth` 行

此解决方案取决于head将打开文件描述的文件偏移量保留在最后报告的行之后 - 我相信head当标准输入从文件重定向时 GNU 确实会这样做

n=200; tmpfile=$(mktemp) && { head -n -$n; tail -n -$((n-1)); }<file >"$tmpfile" \
    && mv -- "$tmpfile" file

相关内容