sed:使用单个通知替换文件中的多行

时间:2017-04-03 05:15:10

标签: awk sed

我正在寻找GNU sed的解决方案,但是POSIX sed没问题,awk可以,但可能比必要的更复杂。我更喜欢sed,它应该很容易,但我被卡住了。看起来像一个单行可以做到这一点,不需要创建一个python / bash脚本或任何东西。

我的尝试解决方案

  sed -i '218,226140d; 218i ...REMOVED...' psql.log

这会删除所需的行,但插入会丢失。如果我将插入移动到第217行,我得到:

  sed -i '218,226140d; 217i ...REMOVED...' psql.log

结果:

  ┌────────────┬─────────────────────┬─────────────────┐
  │ col_one    │     col_two         │ column_three    │
  ├────────────┼─────────────────────┼─────────────────┤
  │ CC00CBSNRY │           553854451 │       15003.44  │
  │ CC00CBSNRY │          1334177150 │        5159.57  │
  ...REMOVED...
  │ CC6XDSQGH2 │         42385958605 │ [null]          │ (line 217 in original file)
  │ CC6XJ8YG5C │         24661013005 │ [null]          │ (line 226141 in original file) 
  │ CC6XJ9HGRG │         44946564505 │ [null]          │
  │ CC6XMQW6SJ │         34496719615 │ [null]          │
  └────────────┴─────────────────────┴─────────────────┘

我知道 - 这应该足够好了,但我很生气,我不能让这个简单的单行工作正常。我错过了什么?

问题

我保留psql.log文件作为我正在开发SQL代码的工作的参考。查看查询和结果的迭代非常有用。

问题是,有时我忘记限制输出,查询将生成100k +行的结果,这些结果不是一个有用的参考,我想从我的文件中删除它们,留下一个提醒我的注释查询输出已被删除。

匹配模式会很好,比如每个输出超过50行我可以压缩到前5行和最后5行。但是,我很容易标记我已经吹过的行号在文件中,所以我很高兴只使用sed删除N到M行,并在第N行插入消息...REMOVED...

这是一个示例日志文件,添加的注释在括号中。查询文本可以更改,列数可以是1到100或更多:

  ...
  ********* QUERY **********
  select      *
  from        table
  where       rnk <= 3
  **************************

  ┌────────────┬─────────────────────┬─────────────────┐
  │ col_one    │     col_two         │ column_three    │
  ├────────────┼─────────────────────┼─────────────────┤
  │ CC00CBSNRY │           553854451 │       15003.44  │
  │ CC00CBSNRY │          1334177150 │        5159.57  │
  │ CC6XDSQGH2 │         42385958605 │ [null]          │ (line 217)
  │ CC6XF2SVWT │         13182280615 │ [null]          │
  (many rows)

  │ CC6XF2XWDT │           995086081 │ [null]          │
  │ CC6XFX3TL1 │         25195177405 │ [null]          │
  │ CC6XJ8YG5C │         24661013005 │ [null]          │  (line 226141)
  │ CC6XJ9HGRG │         44946564505 │ [null]          │
  │ CC6XMQW6SJ │         34496719615 │ [null]          │
  └────────────┴─────────────────────┴─────────────────┘
  (225926 rows)

  ********* QUERY **********
  /* another query begins */

  select * from table where X = 1 limit 20;

  /* well done you remembered to limit the output */
  **************************
  ...

可接受的输出

查询文本应该都不受影响,并保留顶部/底部三行输出。添加了注释...REMOVED...,删除了第218行到第226140行:

  ********* QUERY **********
  select      *
  from        table
  where       rnk <= 3
  **************************

  ┌────────────┬─────────────────────┬─────────────────┐
  │ col_one    │     col_two         │ column_three    │
  ├────────────┼─────────────────────┼─────────────────┤
  │ CC00CBSNRY │           553854451 │       15003.44  │
  │ CC00CBSNRY │          1334177150 │        5159.57  │
  │ CC6XDSQGH2 │         42385958605 │ [null]          │ (line 217 in original file)
  ...REMOVED...
  │ CC6XJ8YG5C │         24661013005 │ [null]          │ (line 226141 in original file)
  │ CC6XJ9HGRG │         44946564505 │ [null]          │
  │ CC6XMQW6SJ │         34496719615 │ [null]          │
  └────────────┴─────────────────────┴─────────────────┘
  (225926 rows)

  ********* QUERY **********
  (etc just like example above)

更新

  • 边框来自我的.psqlrc \pset border 2
  • 因此,取决于字符的解决方案是脆弱的,但确定
  • 随着时间的推移,我了解到手动标记行号非常耗时,因此最佳解决方案需要模式匹配

4 个答案:

答案 0 :(得分:2)

有一个例子'每个输出超过50行我可以压缩到前5行和后5'。

使用测试输入:

$ seq 160 | awk -vstart=10 -vmax=50 -vleft=5 '{if(NR < start) {print; next} {i++; if(i <= left || i > max - left){print}; if(i == left + 1){print "...REMOVED..."}if(i == max){i = 0}}}'

如果您将脚本放入文件中,请将其存储到squash.awk

BEGIN {
    start=10;
    max=50;
    left=5;
}

{
    if(NR < start) {
        print;
        next
    }
    i++;
    if(i <= left || i > max - left) {
        print
    }
    if(i == left + 1) {
        print "...REMOVED...";
    }
    if(i == max) {
        i = 0
    }
}

进行测试:

$ seq 160 | awk -f squash.awk
  1. 变量start是压缩线开始的行号。
  2. 变量max是最大行数(在您的示例中为50)。
  3. 变量leftmax首先和最后一行剩余的行数。
  4. if(NR < start) {print; next}如果行号小于start(在我们的例子中为10),我们只打印它们并转到下一行。 在这里你可以设置任何条件来跳过挤压。
  5. i++它是行计数器增量。
  6. if(i <= left || i > max - left){print}如果行数小于5或更多,则max - 5 - 打印出来。
  7. if(i == left + 1){print "...REMOVED..."}当我们开始跳过行时 - 输入“... REMOVED ...”消息
  8. if(i == max){i = 0}如果行计数器达到max,则将其归零

答案 1 :(得分:1)

一个在awk中:

$ awk '
/^  └/ {                  # at the end marker
    for(j=1;j<=6;j++)     # output from the buffer b the wanted records
        print b[j]
    for(j=(i-2);j<=i;j++) 
        print b[j]
    delete b              # reset buffer
    i=0                   # and flag / counter
} 
/^  ┌/ || i {             # at the start marker or when flag up
    b[++i]=$0             # gather records to buffer
    next
} 1' file                 # print records which are not between the markers

答案 2 :(得分:1)

这可能适合你(GNU sed):

sed -r '/\o342[^\n]*$/{:a;N;//ba;s/^(([^\n]*\n){6}).*((\n[^\n]*){5})$/\1  ... REMOVED ...\3/}' file

仅关注将始终包含八进制值342的表数据。在模式空间中收集表格行,替换所需的值... REMOVED ...并打印。所需字符串上方和下方的行数可在此处更改为6(标题+3行)和5(必需字符串+3行+表计数)。

要更改范围,请使用:

sed 'm,nc ... REMOVE ...' file # where m,n from and to line numbers

或:

sed -e 'ma ...REMOVE ...' -e 'm,nd' file

N.B。 d命令终止以下任何命令。

答案 3 :(得分:0)

sed手册页比初看起来更有帮助。 [addr]c命令正是所需的(注意忽略c后的空格)

sed -i '218,226141c ...REMOVED...' psql.log

所以有已知行号的解决方案。

是否有人想提供不知道行号的通用解决方案?可能awk可能是更好的工具,但也许sed可以删除太长的输出。