使用`sed`将代码块中的文本替换为代码块顶部的命令输出

时间:2018-10-26 12:50:31

标签: bash awk sed

我有一个markdown文件,其中包含类似于以下示例的代码片段:

```
$ cat docs/code_sample.sh

#!/usr/bin/env bash
echo "Hello, world"
```

这意味着在docs/code_sample.sh位置有一个文件,其内容为:

#!/usr/bin/env bash
echo "Hello, world"

我想用sed来解析markdown文件(awkperl也可以)并将代码段的底部替换为上述bash命令求值的内容,例如cat docs/code_sample.sh计算得出的结果。

3 个答案:

答案 0 :(得分:3)

抢救Perl!

perl -0777 -pe 's/(?<=```\n)^(\$ (.*)\n\n)(?^s:.*?)(?=```)/"$1".qx($2)/meg' < input > output
  • -0777将整个文件插入到内存中
  • -p在处理后打印输入
  • s/PATTERN/REPLACEMENT/sed中的替换类似
  • /g在全球范围内替换,即可以替换的次数
  • /m使^匹配每行的开头,而不是整个输入字符串的开头
  • /e将替换项评估为代码
  • (?<=```\n)的意思是“在三个反引号和一个换行符之前”
  • (?^s:.*?)也会更改.的行为以匹配换行符,因此它(由于*?而节俭地)匹配了其余的预格式化块
  • (?=```)的意思是“后跟三个反引号”
  • qx在shell中运行参数并返回其输出

答案 1 :(得分:1)

如果您的GNU版本带有e命令,那么仅sed解决方案会更容易。

也就是说,这是一个快速,简单且有点笨拙的版本,我打掉了这个版本,不必费心检查上一行或下一行的值-只是假设您的格式是好的,并且没有任何循环或任何其他东西其他。不过,对于我的示例代码,它仍然有效。

我首先制作了abx这两个标记文件。

$: cat a
#! /bin/bash
echo "Hello, World!"

$: cat b
#! /bin/bash
echo "SCREW YOU!!!!"

$: cat x
```
$ cat a

foo
   bar
" b a z ! "
```
```
$ cat b

foo
   bar
" b a z ! "
```

然后我写了s脚本sed

$: cat s
#! /bin/env bash

sed -En '

 /^```$/,/^```$/ {

  # for the lines starting with the $ prompt
  /^[$] / {
    # save the command to the hold space
    x
    # write the ``` header to the pattern space
    s/.*/```/
    # print the fabricated header
    p
    # swap the command back in
    x
    # the next line should be blank - add it to the current pattern space
    N
    # first print the line of code as-is with the (assumed) following blank line
    p
    # scrub the $ (prompt) off the command
    s/^[$] //
    # execute the command - store the output into the pattern space
    e
    # print the output
    p
    # put the markdown footer back
    s/.*/```/
    # and print that
    p
  }

  # for the (to be discarded) existing lines of "content"
  /^[^`$]/d

}
' $*

它可以完成工作,并且可能会让您入门。

$: s x
```
$ cat a

#! /bin/bash
echo "Hello, World!"
```
```
$ cat b

#! /bin/bash
echo "SCREW YOU!!!!"
```

很多警告-最好检查$后面是否有一行反引号,然后是一个空行,也许要确保文件中没有虚假的东西可以被执行...但是这样做(GNU)sed向您询问的内容。

祝你好运。

答案 2 :(得分:1)

使用getline的一种罕见情况是合适的:

$ cat tst.awk
state == "importing" {
    while ( (getline line < $NF) > 0 ) {
        print line
    }
    close($NF)
    state = "imported"
}

$0 == "```" { state = (state ? "" : "importing") }

state != "imported" { print }

$ awk -f tst.awk file

有关获取热线的用途和注意事项,请参见http://awk.freeshell.org/AllAboutGetline