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

时间:2017-03-25 00:17:00

标签: bash sed multiline

我有大约一百个包含Latex片段的Markdown文件,如下所示:

<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>标签都标记为“任务”,这将是微不足道的,但我有'journal'和'highlight'的类似div。我需要一个只有当前面的</div>具有类或ID“任务”时才会将</task>更改为<div>的流程,同样适用于“journal”和“highlight”。

浏览了Stack Overflow一段时间后,我发现许多多行搜索和替换的例子几乎可以做我想做的事情,但语法(特别是对于sed)很难解开我无法适应它以上情况。我的下一个选择是写一个bash脚本逐行循环,但我觉得这可能太脆弱了。

干杯

伊恩

4 个答案:

答案 0 :(得分:0)

这应该可以解决问题:

$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;
  • 循环内部的
  • 只是将行收集到模式空间中:N;
  • 在循环结束时只需打印:p;}
  • 使用扩展正则表达式调用并且没有默认打印
    (我的是一个windows / msys sed,只是你知道):$msys\bin\sed -En

答案 1 :(得分:0)

不需要循环。只需管道文件......

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

/Task仅在sed内编辑名称为Task的行。

使用s/NAME/NEWNAME/逐个替换某些文字 添加.*将替换此时开始的所有文本。

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

第二个命令(在;之后)将</div>替换为</task>。它像以前一样是同一个命令的一部分。这次的不同之处在于/(斜杠)将由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>

答案 2 :(得分:0)

以下awk命令通常适用于以下假设:

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

  • 属性全部使用" - 引用。

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

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

  • 处理开场div代码:

    • 模式/^<div / && NF > 1仅匹配以(^<div开头的行和(&&)包含class属性({{ 1}})

    • NF > 1从第二个字段中提取tag=$2; sub("\".*", "", tag)属性值,方法是替换第一个class(属性值的结束")中的所有内容空字符串,仅在变量"中有效保留属性值。

    • tag将属性值打印为替换开始标记。

    • printf "<%s>\n", tag跳过脚本的其余部分并移至下一个输入行。

  • 处理结束next代码:

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

    • tag != ""打印新的结束标记。

    • printf "</%s>\n", tag重置最新的替换代码,以便任何后续的tag=""元素都没有div属性也不会被意外重命名。

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

  • 所有其他行:

    • next只是按原样打印所有其他行。 (11的常见Awk简写:模式{ print },解释为布尔值,按定义为true,没有关联操作的模式1打印输入行默认情况下)。

答案 3 :(得分:0)

这可能适合你(GNU sed):

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

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

N.B。替代品存储在由v

分隔的shell变量|