【问题标题】:How to replace all dollar signs before all variables inside a double-quoted string with sed?如何用sed替换双引号字符串中所有变量之前的所有美元符号?
【发布时间】:2019-07-18 01:32:35
【问题描述】:

我在替换 bash 中的字符串中的变量时遇到问题。比如我想替换

"test$FOO1=$FOO2" $BAR

与:

"test" .. FOO1 .. "=" .. FOO2 .. "" $BAR

我试过了:

sed 's/\$\([A-Z0-9_]\+\)\b/" .. \1 .. "/g'

但我不想在双引号字符串之外以相同的方式替换变量,例如喜欢:

if [ $VARIABLE = 1 ]; then

只需要替换

if VARIABLE then

有没有办法只替换 inside 的双引号?

背景:
我要convert a bash script into Lua script

我知道,以这种方式转换所有可能的 shell 脚本并不容易,但我想要实现的是用 Lua 命令替换所有基本语言结构并替换所有变量和条件。手动将 bash 转换为 Lua 时,此处的自动化将节省大量工作

【问题讨论】:

  • [ $VARIABLE = 1 ] 无论如何都是一个错误;它应该[ "$VARIABLE" = 1 ]。您不能只看引用的内容和未引用的内容。有效地在两种语言之间进行翻译意味着解析源代码以构建抽象语法树,然后使用该树生成目标语言的代码。
  • 例如,你会用if [ "$FOO -- $BAR" = "hello -- there -- world" ];这样的东西做什么?
  • 当然不会很完美,但是通过正确的bash源码准备,在自动翻译到Lua方面可以实现很多,省去很多人工
  • [ $VARIABLE = 1 ] 并不总是需要引号:取决于 VARIABLE 的来源
  • 也许这个项目会对你的总体目标有帮助,而不是这个具体问题:github.com/3scale/apicast-cli/issues/1

标签: regex string bash sed


【解决方案1】:

这可能对你有用(GNU sed):

sed -E ':a;s/^([^"]*("[^"$]*"[^"]*)*"[^"$]*)\$([^" ]*) /\1" .. \3  .. " /;ta;s/^([^"]*("[^"$]*"[^"]*)*"[^"$]*)\$([^"]*)"/\1" .. \3/;ta' file

当更改双引号内的内容时,首先我们必须通过任何不需要需要更改的双引号字符串。这意味着使用 ^ 元字符将正则表达式锚定到行首,并迭代正则表达式直到所有情况都不存在。

首先,从行首删除零个或多个不是双引号的字符。

其次,消除不包含感兴趣字符 (TCOI) 的双引号字符串,即$,后跟零个或多个非双引号字符,零次或多次。

第三,消除双引号后跟零个或多个不是双引号或 TCOI 的字符,即$

以下字符(如果存在)必须是 TCOI。在反向引用 \1 中对之前的整个字符串集合进行分组。

在 TCOI 之后,可以对一个或多个条件进行分组。在上面的示例中,第一个条件是变量(以 TCOI 开头)后跟一个空格。第二个条件是变量后面直接跟"。因此,这需要两个替换命令,ta 命令,当替换成功时分支到标识为 a 的循环。

注意if [ $VARIABLE = 1 ]; then 情况可以在同一个 vien 中处理,这里 [ 是开始双引号,] 是结束双引号。

附: TCOI 是$,这也是正则表达式中的元字符,表示行尾,因此必须引用它,例如\$

附言不要忘记引用[]。如果引用不是你的东西,那么将字符括在 [x] 中,其中 x 是要引用的字符。

编辑:

sed -E ':a;s/^([^"]*("[^"$]*"[^"]*)*"[^"$]*)\$([[:alnum:]]*)/\1" .. \3  .. "/;ta' file

由于原始示例已被 OP 替换,因此这里是基于新示例的解决方案。

【讨论】:

    【解决方案2】:

    使用组和递归

    sed -e ':a' -e 's/^\(\([^"]*\("[^"]*"\)*\)*\)\("[^$"]*\)[$]\([A-Z0-9_]\{1,\}\)/\1\4 .. \5 .. /;t a'
    
    1. 在字符串中与前一部分隔离 ^\(\([^"]*\("[^"]*"\)*\)*\) 第 1 组
    2. 在第 4 组(前缀)和 5(var 名称)中选择用 s\("[^$"]*\)[$]\([A-Z0-9_]\{1,\}\)' 隔离的字符串中的 var 内容
    3. \1\4 .. \5 ..随心所欲地改变
    4. 在发生更改时重复此操作:at a

    使用 gnu sed,您可以将命令缩减为(不需要 -e 来定位标签 a):

    sed ':a;s/^\(\([^"]*\("[^"]*"\)*\)*\)\("[^$"]*\)[$]\([A-Z0-9_]\{1,\}\)/\1\4 .. \5 .. /;t a'
    

    假设字符串中没有引号(转义一个)。如果是这样,则需要第一次通过更改它们并在主要修改后将它们放回原处。

    【讨论】:

      【解决方案3】:

      用于 shell 的 bash 词法分析器!?

      我很抱歉:我只是发布这个答案来警告你错误的方式!

      阅读语言是一个一致的lexer的工作,而不是sed或任何基于regex的工具! !

      请参阅GNU BisonBerkeley Yacc (byacc)

      您可以查看 的源代码以了解脚本的阅读方式!

      以这种方式坚持会很快带你进入大剧本,然后进一步解决无法解决的问题。

      【讨论】:

        【解决方案4】:

        这与用于多字​​符 RS、RT 和 gensub() 的 GNU awk 显示了一种分离然后操作带引号的(在 RT 中)和不带引号的(在 $0 中)字符串作为起点的方法:

        $ cat tst.awk
        BEGIN { RS="\"[^\"]*\""; ORS="" }
        {
            $0 = gensub(/\[\s+[$]([[:alnum:]_]+)\s+=\s+\S+\s+];/,"\\1","g",$0)
            RT = gensub(/[$]([[:alnum:]_]+)"/,"\" .. \\1","g",RT)
            RT = gensub(/[$]([[:alnum:]_]+)/,"\" .. \\1 .. \"","g",RT)
            print $0 RT
        }
        
        $ awk -f tst.awk file
        "count: " .. FOO .. " times " .. BAR
        if VARIABLE then
        

        上面是在这个输入文件上运行的:

        $ cat file
        "count: $FOO times $BAR"
        if [ $VARIABLE = 1 ]; then
        

        注意:这种将字符串与正则表达式匹配的方法始终只是基于提供的示例的最佳努力,您需要一个 shell 语言解析器来稳健地完成这项工作。

        【讨论】:

        猜你喜欢
        • 2011-07-18
        • 2018-01-15
        • 1970-01-01
        • 2013-09-24
        • 1970-01-01
        • 2018-06-19
        • 2022-12-22
        • 1970-01-01
        • 2016-02-09
        相关资源
        最近更新 更多