【问题标题】:Shell Script: Unable to replace regex matched group in yml fileShell 脚本:无法替换 yml 文件中的正则表达式匹配组
【发布时间】:2020-01-02 09:32:31
【问题描述】:

我提到了多个答案,但没有一个对我的问题有帮助,因此提出了一个新问题。 以下是详细信息:

echo $testPorts
output:    - "4441:4441" - "5905:5900"
  • 现在我需要用上面的替换 test.yml 文件中可用的端口
  • 以与 test.yml 相同的格式输出。

    Regex: (ports:)(\n.*\n.*)

  • 需要在 test.yml 和 my 中用上述端口替换第二组

  • 预计 yml 将是
    version: '2'
    services:
        ports:
          - "4441:4441"
          - "5905:5900"

我花了这么多时间仍然无法找出解决方案。

我尝试过的命令:(它们都不起作用)

sed -E 's/(ports:)(\n.*\n.*)/\2 ${'testPorts'}/g' test.yml

sed -E 's/(ports:)(\n.*\n.*)/\2 $'testPorts'/g' test.yml

sed -r 也不起作用。

原始 test.yml:

version: '2'
services:
    ports:
      - "4444:4444"
      - "5901:5900"

【问题讨论】:

  • 你能显示echo "$testPorts"的输出吗? sed 一次解析 一行,要在 sed 中读取下一行,请在 sed 中使用 nN 命令。您是否希望 ports 后面只有两行需要替换,或者行数可能不同?
  • @KamilCuk,输出为“4441:4441” - “5905:5900” 行数将保持 2 只是不会不同。
  • 请编辑您的问题并在帖子中包含相关信息。换行符在 cmets 中不可见。 Sed 有由换行符终止的命令。如果您的testPorts 变量包含换行符,则需要使用另一种方法。可能确实如此,但您的echo $testPorts 并没有说太多,因为它没有被引用。请在问题中包含相关信息。
  • 你的引用是错误的。你需要这样引用:'s/(ports:)(\n.*\n.*)/\2 '"${testPorts}"'/g'
  • @ceving,它给出了 sed: -e expression #1, char 24: unterminated `s' command

标签: linux shell unix sh


【解决方案1】:

下面的代码只等待ports: 行,然后忽略接下来的两行并打印testPorts 变量的内容。我假设testPorts 变量在每行的内容前面包含适当数量的制表符或换行符。

下面将testPorts 变量的内容保存到某个文件中,没有尾随换行符。然后sed 等待包含ports: 的行。如果该行包含端口,则将其打印,接下来的两行将附加到模式空间并删除。然后将临时文件的内容附加到输出中。因为echo 输出了换行符,我在输出中遇到了一些问题,所以我在添加端口后也阅读了下一行并删除了额外的换行符。

echo "$testPorts" > tempfile.txt
sed '/ports:/{ p;N;N;s/.*//; r tempfile.txt'$'\n'' N; s/\n//; }' input.txt

sed 中的r 命令后面的换行符是必需的,因为sed 中的命令由换行符分隔(;“命令”将被解析为文件名的一部分)。有关sed 脚本的更多信息可以从this introduction 学习。 $'\n' 是换行符 - 它使用 ANSI-C quoting

可以按照您的意愿进行操作 - 即。遇到ports: 后匹配两个换行符。为此,您必须将 testPorts 变量中的换行符替换为两个字符 - \n。然后,您可以在 GNU sed 中使用带有 -z 选项的该变量来解析整个文件。因为正则表达式是贪婪的,所以您必须明确定义正则表达式以匹配,直到空格匹配一行。 <<<here string 的 bash 扩展。我可以用 posix-ish sed 编写一个脚本来匹配这些行并替换它们,但我觉得这不值得在这里付出努力。

testports_escaped=$(sed -z 's/\n/\\n/g' <<<"$testPorts")
sed -z 's/\([[:space:]]*ports:[^\n]*\n\)[^\n]*\n[^\n]*\n/\1'"$testports_escaped"'/' input.txt

但由于该操作只是删除ports: 之后的两行,所以我会这样做,看起来最冗长:

{
    sed '/ports:/q'
    read
    read
    printf "%s\n" "$testPorts"
    cat
} < input.txt

我在 awk 中的尝试如下所示:

awk -v v="$testPorts" '/ports:/{ print; getline; getline; print v; getline; }1' input.txt

我尝试测试这些 sn-ps in repl。请注意,不建议使用正则表达式修改 yaml 等结构化文本格式 - 最好使用 yaml 语法“感知”工具。

附言。您可以在更多 posix-sh sed 中使用正则表达式 ports:.*\n.*\n.*(不带尾随 \n),方法是在模式缓冲区中缓冲 3 行并一次仅打印一行,同时保留空间改组,但我发现它不值得这里的努力:

sed -n 'N;N;
  : a; {
    /ports:.*\n.*\n.*/{
            s/.*/ports:/
            r tempfile.txt'$'\n''
            b end
        }
        $ b end
        h
        s/\n.*//
        p
        g
        s/[^\n]*\n//
        N
    }; b a
    : end ; {
        : all
            p
            $ q
            n
        b all
    }
' input.txt

【讨论】:

  • 我试过{ sed '/container_name:/q' printf "$testPorts" &gt;&gt;docker-compose.yml } &lt; docker-compose.yml test.yml 被编辑并且输出在下面,这给了我 yml 文件中的语法错误。我希望端口和它的键作为 container_name version: '2' services: container_name: test11 ports: - "4441:4441" - "5905:5900" 的列表我的预期文件应该是格式如下:version: '2' services: container_name: test11 ports: - "4441:4441" - "5905:5900" 我也找不到 /q 在 sed '/container_name:/q' 中有什么用处
  • 然后呢?有问题吗? sed introduction, chapter about 'q' 我什至发布了一个链接到 repl 的示例 - 学习它。
猜你喜欢
  • 1970-01-01
  • 2014-05-12
  • 1970-01-01
  • 1970-01-01
  • 2012-05-16
  • 1970-01-01
  • 1970-01-01
  • 2012-11-23
  • 2022-08-10
相关资源
最近更新 更多