仅在包含字符串的文本文件中使用 sed 或 ed 交换行?

仅在包含字符串的文本文件中使用 sed 或 ed 交换行?

我需要交换这些行,仅当匹配的字符串都在两行中时:

前:

REF*CE*-------------------------
REF*1W*-------------------------

后:

REF*1W*-------------------------
REF*CE*-------------------------

我尝试了这个,它不起作用:

ed -s testfile.txt <<<$'/REF*CE*/-0,/REF*CE*/+0m/REF*1W*/\nw\nq'

答案1

sed -e :a -e '$!N;s/^\(REF\*CE.*\)\n\(REF\*1W.*\)/\2\n\1/;ta' -e 'P;D' <testfile.txt 
  1. 如果我们不在最后一行,则附加下一行。
  2. 仅当匹配时才会在当前行上进行替换substring containing pattern 1 + newline + substring containing pattern 2。替换会翻转两个子字符串。替换后返回标签:a。
  3. 如果没有匹配则按原样打印模式空间。然后删除模式空间并再次开始循环。

带有一些周围线条的示例...

In:

    XEF*CE*------------------------- 
    REF*CE*------------------------- 
    REF*1W*------------------------- 
    REF*2W*------------------------- 

Out:


    XEF*CE*------------------------- 
    REF*1W*------------------------- 
    REF*CE*------------------------- 
    REF*2W*------------------------- 

更一般地适用于任何模式 1 和模式 2

sed -e :a \
    -e "\$!N; s/^\(.*${pattern1}.*\)\n\(.*${pattern2}.*\)/\2\n\1/;ta" \
    -e 'P;D' < inputfile

答案2

如何交换与特定正则表达式匹配的两行(可能相距很远)的通用解决方案ed

  1. 复制一行到第二行之后。
  2. 移动第二行位于原始第一行之后。
  3. 删除第一个原始行。

或者,使用ed编辑命令:

  1. /pat1/t/pat2/
  2. ?pat2?m/pat1/
  3. ?pat1?d

示例为文件

CLP*815900102*2*489.8*101.5*82.29*13*PVJLS03YP0000*13*7
AMT*AU*489.8
REF*6R*00000000002
DTM*472*20160528
CAS*OA*23*306.01
CAS*PR*2*82.29
SVC*HC:99212:25*489.8*101.5**1
AMT*B6*411.43

我们想将第一AMT行与第二CAS行交换。 pat1将会是^AMT\*AU并且pat2将会是^CAS\*PR。请注意,我们需要对其进行转义,*以便在正则表达式中按字面意思进行处理。

我在下面注释了更改,以便更容易看到它们。指示XXX每次操作后文件中的当前位置。

  1. /^AMT\*AU/t/^CAS\*PR/产生

    CLP*815900102*2*489.8*101.5*82.29*13*PVJLS03YP0000*13*7
    AMT*AU*489.8        <-- Line copied *from* here
    REF*6R*00000000002
    DTM*472*20160528
    CAS*OA*23*306.01
    CAS*PR*2*82.29
    AMT*AU*489.8        <-- Line copied *to* here (XXX)
    SVC*HC:99212:25*489.8*101.5**1
    AMT*B6*411.43
    
  2. ?^CAS\*PR?m/^AMT\*AU/产生

    CLP*815900102*2*489.8*101.5*82.29*13*PVJLS03YP0000*13*7
    AMT*AU*489.8
    CAS*PR*2*82.29      <-- line moved here (XXX)
    REF*6R*00000000002
    DTM*472*20160528
    CAS*OA*23*306.01
    AMT*AU*489.8        <-- line previous to this deleted
    SVC*HC:99212:25*489.8*101.5**1
    AMT*B6*411.43
    
  3. ?^AMT\*AU?d产生

    CLP*815900102*2*489.8*101.5*82.29*13*PVJLS03YP0000*13*7
    CAS*PR*2*82.29      <-- the line before this was removed (XXX)
    REF*6R*00000000002
    DTM*472*20160528
    CAS*OA*23*306.01
    AMT*AU*489.8
    SVC*HC:99212:25*489.8*101.5**1
    AMT*B6*411.43
    

作为一个容易记住的“一句话”:

pat1='^AMT\*AU'; pat2='^CAS\*PR'; printf '/%s/t/%s/\n?%s?m/%s/\n?%s?d\nwq\n' "$pat1" "$pat2" "$pat2" "$pat1" "$pat1" | ed -s file

请注意,通过第二次运行完全相同的操作,这是可逆的,即第一个或第二个模式并不重要。

答案3

我们可以通过将匹配的行放入保持缓冲区,读取下一行并打印它,然后将保持缓冲区与模式缓冲区交换并再次打印来获得所需的效果。

bash-4.3$ sed -n '/^REF\*CE/!p;/^REF\*CE/{h;n;p;x;p}' input.txt
some line here
REF*BB*106497026---------------
REF*1W*723266637---------------
REF*CE*NEW JERSEY--------------
SVC*HC^S5102*78.5*78.5**1------
another line there

答案4

我有这样的简单解决方案:假设您想将文件中包含“XXXX”的行与包含“YYYY”的行交换,则只提供一行包含“XXXX”并且只有一行包含“YYYY”

示例文件如下: ssss dddd
aaaa ffff
ddd rrrr
ddddd XXXX
ddddde
ffff
ffff
fff
eeee YYYY
ghghgh
hhhhh

我的“sed”命令是 sed 's/XXXX/ZZZZ/g;s/YYYY/XXXX/g;s/ZZZZ/YYYY/g' 请记住“ZZZZ”字符串不应出现在文件中。

相关内容