【问题标题】:Use "sed" to reorder two consecutive lines when some criteria is true当某些条件为真时,使用“sed”对两个连续的行重新排序
【发布时间】:2020-08-21 20:05:42
【问题描述】:

输入文本是:

Lines_with_no_search_string_1
...,"service_id":222,"...1
...,"service_id":111,"...2
Lines_with_no_search_string_2
...,"service_id":111,"...3
...,"service_id":222,"...4
Lines_with_no_search_string_3
...,"service_id":222,"...5
Lines_with_no_search_string_4
...,"service_id":111,"...6
Lines_with_no_search_string_5

注意事项:

  • “...”是不包含“service_id”的随机字符串。 (“...”后面的数字仅用于识别线序!)
  • “Lines_with_no_search_string”是一行或多行不包含“service_id”的行。
  • 最多只有 2 行包含“service_id”,即包含“111”和“222”的连续行。

标准:

  • 不包含“service_id”的行
    • 打印出来(行号相同)。
  • 包含“service_id”的单行
    • 如果为“111”,则按原样打印(行号相同)。
    • 如果为“222”,则按原样打印,并在行首插入字符串“NONE”(行号相同)。
  • 包含“service_id”的连续两行
    • 如果先出现“111”,则按原样打印两行(行号相同)。
    • 如果“222”在前,则以相反的顺序打印,即先打印“111”和“222”(与原始行号相比,行号相反)。

想要的输出是:

Lines_with_no_search_string_1
...,"service_id":111,"...2          (*)
...,"service_id":222,"...1          (*)
Lines_with_no_search_string_2
...,"service_id":111,"...3
...,"service_id":222,"...4
Lines_with_no_search_string_3
"NONE"...,"service_id":222,"...5    (*)
Lines_with_no_search_string_4
...,"service_id":111,"...6
Lines_with_no_search_string_5

(*) 显示已修改的行。

我想使用“sed”的原因是我有一个很长的命令行导致上述输入,我更喜欢继续使用管道(“|”)来获得最终输出。 如果有所谓的单行命令,除了“sed”之外还有其他命令可以提供所需的输出,我很乐意使用它。

我在不同的“sed”组合上花了很多时间,但没有弄对。

我的最后一种方法是基于以下替换“\n”的代码:

sed ':a;N;$!ba;s/\n/some_string/g' # Note: for csh/tcsh, "!" needs to be escaped.

我尝试将两个连续的行(当 222 行后跟 111 行)放在“\1”和“\2”中,并将它们反转:

sed ':a;N;$!ba;s/\(.*[^2][^2][^2]*222.*\)\n\(.*[^1][^1][^1]*111.*\)/\2\n\1/g'

但它并没有像我想的那样工作:

...,"service_id":111,"...6
Lines_with_no_search_string5
Lines_with_no_search_string1
...,"service_id":222,"...1
...,"service_id":111,"...2
Lines_with_no_search_string2
...,"service_id":111,"...3
...,"service_id":222,"...4
Lines_with_no_search_string3
...,"service_id":222,"...5
Lines_with_no_search_string4

我现在已经意识到,我的方法所基于的解决方案会遍历整个文件。 这就是上面输出乱码的原因。

如果有人能提出解决方案,请不胜感激。

【问题讨论】:

  • 欢迎来到 Stack Overflow。 SO 是面向专业和热情的程序员的问答页面。请在您的问题中添加您自己的代码。您应该至少展示自己为解决这个问题所做的研究。
  • 请使用tour 并阅读How to Ask。您应该提供minimal reproducible example

标签: linux sed


【解决方案1】:

这很简单:

sed '
# lines that dont conatin service_id
/service_id/!{
    # Print out (with same line numbers).
    b
}
N;

# single line that contains service_id
/service_id[^\n]*\n[^\n]*service_id/!{
    # If "111", print out as is (with same line number).
    /222/{
       # If "222", print out as is, with a string "NONE" inserted at the beginning of the line
        s/^/NONE/
    }
    b
}

# Two consecutive lines that contain "service_id"
/111[^\n]*\n[^\n]*$/{
    # If "111" comes first, print out both lines as is (with same line number).
    b
}
# If "222" comes first, print out in reverse order
s/^\([^\n]*\)\n\([^\n]*\)$/\2\n\1/
'

这有一个错误,无法处理超过 2 行的 service_id 连续行,但 There are only maximum 2 consecutive lines that contain "service_id" 应该没问题。并且正则表达式需要改进以匹配更多的 OP,但我希望这并不难做到。我也不关心如果输入中没有111222 会发生什么。

可爱的oneliner:

sed '/service_id/!b; N;/service_id[^\n]*\n[^\n]*service_id/!{ /222/{ s/^/NONE/; }; b; }; /111[^\n]*\n[^\n]*$/b; s/^\([^\n]*\)\n\([^\n]*\)$/\2\n\1/'

Tetsted on repl

【讨论】:

  • 我将所有内容放在一行中: sed ' /service_id/!{ b } N; /service_id[^\n]*\n[^\n]*service_id/!{ /222/{ s/^/NONE/ } b } /111[^\n]*\n[^\n]*$ /{ b } s/^([^\n]*)\n([^\n]*)$/\2\n\1/ ' 并执行。结果: sed: -e expression #1, char 17: extra characters after command
  • 那是因为你把它放在了一行...} 需要一个;。好吧,你不能只是“我做了那个,它坏了”,是的,它确实,不知道你期望什么?
  • 完美运行:)。作为信息:投票已记录,但不公开,因为我的代表还不到 15 岁。
猜你喜欢
  • 1970-01-01
  • 2022-01-02
  • 1970-01-01
  • 2011-03-12
  • 1970-01-01
  • 2014-04-28
  • 1970-01-01
  • 2014-07-27
  • 2021-12-08
相关资源
最近更新 更多