【问题标题】:How to swap words after specific word, which can be in one line or can be in differen lines using sed?如何在特定单词之后交换单词,可以在一行中,也可以使用 sed 在不同行中?
【发布时间】:2019-09-20 12:14:34
【问题描述】:

我有一个文件,其中行开始:

OCCUPY 12 EVERY PIC 32(12).     SUNNY 
OCCUPY 45 EVERY PIC X(21).      SUNNY

有些行可以分成两行:

1      OCCUPY 12 EVERY PIC 32(12).    SUNNY
2              OCCUPY 45 EVERY        SUNNY 
3                   PIC X(21).        SUNNY

所需的输出是:

PIC 32(12) OCCUPY 12 EVERY.    SUNNY
PIC X(21) OCCUPY 45 EVERY.     SUNNY 

所以,我需要将分隔的行合并为一行并交换单词。 所以,对于那些在一行中的,我正在使用命令:

sed 's/\(OCCUPY.*EVERY\) *\(PIC *[^ }*\)\./\2 \1./'
sed -Ez 's/(OCCUPY)\s+([0-9]+)\s+(EVERY)\s+(PIC)\s+([^)]+\))/\4 \5 \1 \2 \3/g'

这些命令有效,但前提是该行在一行中,并且它移动单词 SUNNY 并且点在一个空格之外。 SUNNY 这个词必须在同一个字段中。所以,错误的输出是:

PIC 32(12) OCCUPY 12 EVERY.     SUNNY
PIC X(21) OCCUPY 45 EVERY .   SUNNY 

任何帮助将不胜感激!

【问题讨论】:

  • perl -pe 'unless (/\.\s*\z/) { $_ .= readline; redo } s/^OCCUPY\s+(\S+)\s+EVERY\s+(\S.*)\./$2 OCCUPY $1 EVERY./'?
  • 您没有使用您所说的 sed 命令,因为它包含语法错误。

标签: shell sed scripting


【解决方案1】:

使用 GNU sed 表示 -E-z\s 简写为 [[:space:]]

$ sed -Ez 's/(OCCUPY\s+[0-9]+\s+EVERY)\s+(PIC\s+[^)]+\))/\2 \1/g' file
PIC 32(12) OCCUPY 12 EVERY.
PIC X(21) OCCUPY 45 EVERY.

如果每一行都符合空格/非空格字符串的模式,您可以将其减少为:

$ sed -Ez 's/((\S+\s+){2}\S+)\s+(\S+\s+\S+\))/\3 \1/g' file
PIC 32(12) OCCUPY 12 EVERY.
PIC X(21) OCCUPY 45 EVERY.

或者如果该行可以在任何空白处拆分,而不仅仅是您的示例中显示的位置:

$ sed -Ez 's/(OCCUPY)\s+([0-9]+)\s+(EVERY)\s+(PIC)\s+([^)]+\))/\4 \5 \1 \2 \3/g' file
PIC 32(12) OCCUPY 12 EVERY.
PIC X(21) OCCUPY 45 EVERY.

$ sed -Ez 's/(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+\))/\4 \5 \1 \2 \3/g' file
PIC 32(12) OCCUPY 12 EVERY.
PIC X(21) OCCUPY 45 EVERY.

鉴于您更新的输入/输出脚本仍然有效:

$ cat file
OCCUPY 12 EVERY PIC 32(12).     SUNNY
OCCUPY 45 EVERY
        PIC X(21).      SUNNY

$ sed -Ez 's/(OCCUPY\s+[0-9]+\s+EVERY)\s+(PIC\s+[^)]+\))/\2 \1/g' file
PIC 32(12) OCCUPY 12 EVERY.     SUNNY
PIC X(21) OCCUPY 45 EVERY.      SUNNY

$ sed -Ez 's/((\S+\s+){2}\S+)\s+(\S+\s+\S+\))/\3 \1/g' file
PIC 32(12) OCCUPY 12 EVERY.     SUNNY
PIC X(21) OCCUPY 45 EVERY.      SUNNY

$ sed -Ez 's/(OCCUPY)\s+([0-9]+)\s+(EVERY)\s+(PIC)\s+([^)]+\))/\4 \5 \1 \2 \3/g' file
PIC 32(12) OCCUPY 12 EVERY.     SUNNY
PIC X(21) OCCUPY 45 EVERY.      SUNNY

$ sed -Ez 's/(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+\))/\4 \5 \1 \2 \3/g' file
PIC 32(12) OCCUPY 12 EVERY.     SUNNY
PIC X(21) OCCUPY 45 EVERY.      SUNNY

【讨论】:

  • 嗨,Ed,我正在尝试您提供给我的代码,但问题是它正在改变顺序并且点在前面一个空格,是否可以不改变它们?我更新了我的问题的输出。你能看看吗?谢谢
  • 在询问有关解析/操作文本的问题以提供真正具有代表性的示例输入/输出时,这一点非常重要。您不应期望为一种输入格式编写的脚本适用于另一种输入格式。话虽如此,我回答中的脚本仍然适用于您的新输入格式。请参阅我的答案末尾的更新。
  • 等一下 - 当您的输入记录被分成几行时,您是说“SUNNY”这个词突然出现在记录中间吗?
  • 你之前没有提到任何关于有字段的事情。如果有制表符分隔或固定宽度或任何其他类型的字段,并且换行文本在一个字段中,那么这完全改变了问题,并且使用 awk 而不是 sed 解决方案肯定会更好地处理它。请使用真正具有代表性的示例输入和预期输出创建一个新问题。
  • 好的,我将创建一个新问题。再次感谢您!
【解决方案2】:

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

sed -E '/OCCUPY/{:a;/ PIC/!{N;s/\n\s*//;ta};s/PIC/\n&/;s/(.*) \n([^.]*)/\2 \1/}' file

这侧重于包含 OCCUPY 的行,然后如果同一行不包含 PIC 则追加更多行(删除它们的换行符),直到它包含。

使用引入的换行符作为分隔符,然后使用模式匹配将模式空间中的行重新排列为所需的格式。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-09-18
    • 2019-07-26
    • 1970-01-01
    • 1970-01-01
    • 2019-10-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多