仅当给定文本在其前面时才用文本替换字符串

Replace string with text only when a given text precedes it

我有大约一百个 Markdown 文件,其中包含这样的 Latex 片段:

<div latex="true" class="task" id="Task">

(@) Delete the fourth patterns from your .teach file and your .data files. Remember to change the second line in each so that Tlearn knows there are now only three patterns.

- They should look like [@fig:dataTeach]

</div>

我想用更易于阅读的伪标签替换 <div> 标签,如下所示:

<task>

(@) Delete the fourth patterns from your .teach file and your .data files. Remember to change the second line in each so that Tlearn knows there are now only three patterns.

- They should look like [@fig:dataTeach]

</task>

如果我所有的 <div> 标签都标记 'tasks',这将是微不足道的,但我有类似的 div 'journal' 和 'highlight'。我需要一个过程,仅当前面的 <div> 具有 class 或 id 'task' 时,才会将 </div> 更改为 </task>,同样对于 'journal' 和 'highlight'。

查看 Stack Overflow 一段时间后,我发现许多多行搜索和替换的示例几乎可以完成我想做的事情,但是语法(尤其是 sed)很难理清,我无法适应它用于上述情况。我的下一个选择是编写一个 bash 脚本来逐行循环,但我觉得这可能太脆弱了。

干杯

伊恩

这应该可以解决问题:

$msys\bin\sed -En "s/<div latex=\"true\" class=\"task\" id=\"Task\">/<task>/;T;{:a;N;s/<\/div>/<\/task>/;Ta;p;}" input.txt  

这些是构建基块,以备不时之需:

  • 做一个循环:{:a;
  • 第二次替换触发时结束:s/<\/div>/<\/task>/;Ta;
  • 只启动它,如果第一个替换被触发:
    s/<div latex=\"true\" class=\"task\" id=\"Task\">/<task>/;T;
  • 在循环内只是将行收集到模式 space:N;
  • 在循环结束时打印:p;}
  • 使用扩展的正则表达式调用并且没有默认打印
    (我的是 windows/msys sed,你知道的):$msys\bin\sed -En

不需要循环。只需通过管道传输文件...

sed '/Task/s/<div.*>/<task>/g;s/<\/div>/<\/task>/g'
开头的

/Task 使 sed 只编辑名称为 Task 的行。

使用 s/NAME/NEWNAME/ 可以一个接一个地替换一些文本。
添加 .* 将替换从此时开始的所有文本。

最后但同样重要的是,g 代表全局,将以这种方式编辑所有条目。

第二个命令(在 ; 之后)将用 </task> 替换 </div>。它和以前一样是同一命令的一部分。这次的不同之处在于 /(斜杠)将由 sed 自己使用,如果没有以其他方式声明的话!这可以通过 \(反斜杠)进行存档。

给你。您的文件的输出将如下所示....

<task>

(@) Delete the fourth patterns from your .teach file and your .data files. Remember to change the second line in each so that Tlearn knows there are now only three patterns.

- They should look like [@fig:dataTeach]

</task>

以下 awk 命令在以下假设下一般有效:

  • 所有开始和结束 div 标签都在各自的行上。

  • 属性全部使用"-quoting.

  • 新的标签名称仅来自 class 属性的值(如果规则更清晰,这可以推广)。

awk -F ' class="' '
  /^<div / && NF > 1 { tag=; sub("\".*", "", tag); printf "<%s>\n", tag; next }
  /^<\/div>/ && tag != "" { printf "</%s>\n", tag; tag=""; next }
  1
' file
  • -F ' class="' 有效地将每一行拆分为 class 属性之前(字段 1,</code>)和之后(字段 2,<code>) ,如果存在。因此,只有具有此类属性的行才会有超过 1 个字段 (NF > 1).

  • 正在处理开头 div 标签:

    • Pattern /^<div / && NF > 1 因此仅匹配以 (^) <div 和 (&&) 开头且包含 class 的行属性 (NF > 1)

    • tag=; sub("\".*", "", tag) 从第二个字段中提取 class 属性值,通过替换第一个 " 中的所有内容(结束 "属性值)与空字符串,有效地只在变量 tag.

    • 中保留属性值
    • printf "<%s>\n", tag 打印属性值作为替换开始标记。

    • next 跳过脚本的其余部分并移至下一个输入行。

  • 正在处理结束 div 标记:

    • /^<\/div>/ && tag != "" 与结束 div 标签相匹配,假设在前一个开始标签 (tag != "") 中找到了 class 属性值。

    • printf "</%s>\n", tag 打印新的结束标记。

    • tag="" 重置最近的替换标签,这样任何后续 div 没有 class 属性的元素也不会意外重命名。

    • next 跳过脚本的其余部分并移至下一个输入行。

  • 所有其他行:

    • 1 只是按原样打印所有其他行。 (1{ print } 的常见 Awk shorthand:模式 1,解释为布尔值,根据定义为真,并且没有关联操作的模式 { ... }默认打印输入行)。

这可能适合您 (GNU sed):

v='task|journal|highlight'
sed -ri '/^<div/{:a;N;/^<\/div/M!ba;s/^<.*class="('$v')"[^>]*(.*<\/)div/</}' file1 file2 file3 ...

这会将 div 语句存储在模式 space 中,然后根据预先设置的 shell 变量替换(或不替换)所需的值。

N.B。备选方案存储在 shell 变量 v 中,由 |

分隔