Linux sed';s';N';命令间歇工作

Linux sed';s';N';命令间歇工作,linux,bash,sed,Linux,Bash,Sed,下面是我要格式化的文本块示例: <tr><td></td><td>tear a cat in, to make all split.</td><td></td></tr> <tr><td></td><td class="tdci">The raging rocks</td><td></td></tr> &l

下面是我要格式化的文本块示例:

<tr><td></td><td>tear a cat in, to make all split.</td><td></td></tr>
<tr><td></td><td class="tdci">The raging rocks</td><td></td></tr>
<tr><td></td><td class="tdci">The foolish Fates.</td></tr>
<tr><td></td><td>This was lofty! Now name the rest of the players.</td><td></td></tr>
撕开一只猫,把所有的东西都撕开。
汹涌的岩石
愚蠢的命运。
这是崇高的!现在说出其余球员的名字。
在脚本中使用这两个“sed”命令:

sed -ri '/^<tr><td><\/td><td>/N;s/(\n<tr><td><\/td><td class="tdci">)/\n<tr><td>\&nbsp;<\/td><\/tr>\1/' "$f"   #insert table row with empty data fields (blank line) above first line with 'class="tdci"'
sed -ri '/^<tr><td><\/td><td class="tdci">/N;s/(\n<tr><td><\/td><td>)/\n<tr><td>\&nbsp;<\/td><\/tr>\1/' "$f"   #insert table row with empty data fields (blank line) after last line with 'class="tdci"'
sed-ri'/^/N;s/(\n)/\n\\1/“$f”#在第一行上方插入带有空数据字段(空行)的表行,并使用“class=”tdci“'
sed-ri'/^/N;s/(\n)/\n\\1/“$f”#在带有“class=”tdci“的最后一行之后插入数据字段为空的表行(空行)'
结果如下:

<tr><td></td><td>tear a cat in, to make all split.</td><td></td></tr>
<tr><td>&nbsp;</td></tr>
<tr><td></td><td class="tdci">The raging rocks</td><td></td></tr>
<tr><td></td><td class="tdci">The foolish Fates.</td></tr>
<tr><td></td><td>This was lofty! Now name the rest of the players.</td><td></td></tr>
撕开一只猫,把所有的东西都撕开。
汹涌的岩石
愚蠢的命运。
这是崇高的!现在说出其余球员的名字。
因此,第一个
sed
命令通过使用
class=“tdci”
在第一行上方插入一个空表行来工作,但是几乎相同的第二个
sed
命令意味着在最后一行之后插入一个空表行,而使用
class=“tdci”
则不起作用

我通常为vim保存这些编辑,在多行之间编辑,因为我从来没有遇到过类似命令的问题,但出于某种原因,
sed
N;s/
对我来说总是一帆风顺,就像在本例中,一个实例可以正常工作,而另一个实例则不行。在运行这些命令之前,脚本将删除所有前导/尾随空格和任何Winblowz回车(
\r

由于我有大量的文件要编辑,如果有人能看到任何明显的我做错了的事情,我当然更愿意在脚本中运行

其他详情:


抱歉,我忘了提到我正在Linux(Debian stable)中运行
sed

从小开始!下面是一个更简单的测试用例:

a1
b1
b2
a2
下面是您针对该测试用例翻译的代码,尝试在第一个“b”之前插入
c1
,在最后一个“b”之后插入
c2

sed -ri '/a/N; s/(\nb)/\nc1\1/' file
sed -ri '/b/N; s/(\na)/\nc2\1/' file
正如您所说,第一个命令似乎起作用:

a1
c1
b1
b2
a1
第二个没有,只给出与上面相同的结果,而不是插入
c2

以下是您可能认为会发生的情况,粗体部分不正确:

  • 读取并打印
    a1
  • 读取并打印
    c1
  • b1
    被读取。
    • 它与
      /b/
      匹配,
      b2
      N
      读取
    • 它与
      \na
      不匹配
    • b1
      已打印
  • b2
    被再次读取
    • 它与
      /b/
      匹配,
      a
      N
      读取
    • 它与
      \na
      匹配<代码>c2追加
    • b2\nc2\na已打印
  • 下面是实际发生的情况

  • 读取并打印
    a1
  • 读取并打印
    c1
  • b1
    被读取。
    • 它与
      /b/
      匹配,
      b2
      N
      读取
    • 它与
      \na
      不匹配
    • b1\nb2
      已打印
  • a2
    被读取和打印
    ,因为上面已经读取了
    b2
    这是一个有效的命令:

    sed -ri '/b/ { :b; N; s/\na/\nc2&/; te; P; D; bb; }; :e;' file
    
    在伪代码中——注释中大致对应sed部分——这是:

    if (input.matches("b")) {                               // /b/ {
      while(true) {                                         // :b
        input += "\n" + readline();                         // N
        if(input.matches("\na")) {                          // s/\na/ ..
          input = input.replace("(\na)", "\nc2\1");         // .. \nc2&/
          goto exit;                                        // te
        }
        print(input.substring(0, input.indexOf('\n'));      // P
        input = input.substring(input.indexOf('\n') + 1);   // D
      }                                                     // bb
    }                                                       // }
    :exit                                                   // :e
    
    翻译回您的数据:

    sed -ri '/^<tr><td><\/td><td class="tdci">/ { :b; N; s/(\n<tr><td><\/td><td>)/\n<tr><td>\&nbsp;<\/td><\/tr>\1/; te; P; D; bb; }; :e' "$f"
    
    sed-ri'/^/{:b;N;s/(\N)/\N\\1/;te;P;D;bb;};:e“$f”
    
    演示如何使用
    sed
    执行此操作

    然而,
    sed
    在遇到这些本质上有点程序性的问题时可能会让人大吃一惊,因此这里有一个
    awk
    解决方案,可能更容易理解:

    awk -v blockRegex='^<tr><td><\/td><td class="tdci">' \
        -v lineToInsert='<tr><td>\&nbsp;<\/td><\/tr>' \
      '
        # Print a line BEFORE the FIRST line matching `blockRegex`.
      $0 ~ blockRegex { if (!afterFirst) {print lineToInsert; afterFirst=inBlock=1} }
        # Print a line AFTER the LAST (contiguous) line matching `blockRegex`.
      inBlock && $0 !~ blockRegex { print lineToInsert; afterFirst=inBlock=0 }
        # Print the input line.
      { print }
      ' \
      file
    
    awk-v blockRegex='^'\
    -v lineToInsert='\'\
    '
    #在匹配“blockRegex”的第一行之前打印一行。
    $0~blockRegex{if(!afterFirst){print lineToInsert;afterFirst=inBlock=1}
    #在与“blockRegex”匹配的最后一行(连续)后打印一行。
    块内&&$0!~blockRegex{print lineToInsert;afterFirst=inBlock=0}
    #打印输入行。
    {print}
    ' \
    文件
    
    请注意,这可以进一步优化,但我想让它更简单,以澄清逻辑

    • blockRegex
      作为变量传入(带有选项
      -v
      ),以标识要插入的行前后的连续行块,要插入的行作为变量传入
      lineToInsert
    • $0~blockRegex
      匹配感兴趣行块中的每一行,并打印要插入的行(如果它是块中的第一行),如状态变量
      afterFirst
      所示;状态变量
      inBlock
      表示当前行位于感兴趣的块内
    • inBlock&&0!~blockRegex
      匹配感兴趣块后的第一行并打印要插入的行,然后重置状态变量
    • print
      只按原样打印输入行

    请注意,状态变量的使用依赖于
    awk
    中默认为
    0
    (在布尔上下文中被视为
    false
    ;类似地,非零值的计算结果为
    true
    )。

    有大量的HTML解析器和漂亮的打印机;利用这些。你在什么操作系统上这样做?sed的哪个版本?@devnull我很感激HTML的建议,但是让我们集中精力。我的问题是关于特定“sed”命令的行为;我试图处理的文本很可能是HTML以外的其他格式。@tog:wow!它是有效的,但老实说,我已经迷路了,必须开始阅读你们详细介绍的“sed”选项,以帮助了解如何和为什么;伟大的分析;伪代码缺少字符串替换逗号