【问题标题】:sed command to replace only what's inside a matching set of parenthesessed 命令仅替换匹配的括号内的内容
【发布时间】:2016-07-25 22:02:58
【问题描述】:

我有一些看起来像 OldUtility.getList(obj) 的旧代码已被重构为 obj.getList()。我正在尝试编写一个sed 命令来正确重构我的代码。到目前为止,我所拥有的是:

sed -i '' 's/\(OldUtility.getList(\)\(.*\))/\2.getList()/g'

这样做的问题是它贪婪地抓住了最后一个右括号。这意味着以下情况不起作用:
OldUtility.getList(obj).size()

someFunc(OldUtility.getList(obj), otherObj.otherFunc())

但我不希望它不贪心,因为它还需要处理以下情况:
OldUtility.getList(otherObj.toObj())->otherObj.toObj().getList()

所以问题是我如何让\2 成为OldUtility.getList(...) 括号内的所有内容?

【问题讨论】:

    标签: regex bash sed regex-greedy


    【解决方案1】:

    如果您不想捕获右括号,则应使用[^)]* 而不是.*

    用这个测试过:

    echo "OldUtility.getList(otherObj.toObj()) OldUtility.getList(obj).size() someFunc(OldUtility.getList(obj), otherObj.otherFunc())" | sed -E 's/OldUtility.getList.([^)]*)\)([\)]*)/\1\2.getList()/g'

    命令是sed -E 's/OldUtility.getList.([^)]*)\)([\)]*)/\1\2.getList()/g'

    【讨论】:

    • 谢谢 - 但这还不够。如果你尝试: $ echo "OldUtility.getList(otherObj.toObj())" | sed 's/(OldUtility.getList()([^)]*))/\2.getList()/g' 你会得到:otherObj.toObj(.getList())
    • 你是赢家!谢谢。但是请注意-该命令的第一个副本与第二个副本不同,并且似乎缺少一些括号。但是第二个副本效果很好!
    • 其实第二个是第一个的副本。 OldUtility.getList. 应该是 OldUtility\.getList\(,但这仅在极端情况下才有意义。
    【解决方案2】:

    由于getList(...) 可能多次包含任何级别的嵌套括号,因此您无法使用 sed 解决此问题(无法知道哪个右括号是好的)。这是您可以与 Perl 一起使用的模式(具有匹配嵌套括号的功能):

    OldUtility\.getList\(([^()]*+(?:\((?1)\)[^()]*)*+)\)
    

    详情:

    OldUtility\.getList\( # Note that the literal dot and parenthesis must be escaped
    (            # open capture group 1
        [^()]*+  # all that is not a parenthesis (zero or more)
        (?:           # open a non capturing group
            \((?1)\)  # recursion with the capture group 1 subpattern
            [^()]*
        )*+           # repeat the non-capturing group (zero or more times)
    )
    \)
    

    例子:

    echo 'OldUtility.getList(otherObj.toObj().toString())' | perl -pe 's/OldUtility\.getList\(([^()]*+(?:\((?1)\)[^()]*)*+)\)/$1.getList()/g'
    

    【讨论】:

    • @Sean:正如你所见,全局语法与 sed 语法并没有太大区别(即:s/.../.../x)。 p 参数自动打印该行。要了解的主要内容是递归:您打开一个捕获组,其中子模式包含对其自身的引用((?1))。
    • 我知道这要求很多 - 你可以拒​​绝。但是您可以为我节省大量时间...我还需要能够将另一个模式从OldUtility.addItem(obj, item) 重构为obj.getList().addItem(item),并具有支持嵌套括号的所有相同限制。我正在尝试了解有关 perl 递归的所有信息,以便我可以采用您的答案,但我的猜测是您可以更快地做到这一点
    • @Sean:我原谅你,因为它更难。由于逗号只能出现在递归中(括号内),因此您需要使用条件来测试您是否在递归中:(?(R)A|B) (if recursion then A else B)。结果:s/OldUtility\.addItem\(((?(R)[^()]*+(?:\((?1)\)[^()]*)*+|[^(),]*+(?:\((?1)\)[^(),]*)*+)),\h*/$1.getList().addItem(/g 演示:regex101.com/r/oK4nQ5/1
    • 如果可以,我会请你喝啤酒。非常感谢!
    • @Sean: ((?(R)er|be))(?1) ?
    【解决方案3】:

    你让它变得比需要的更复杂。

    $ echo "OldUtility.getList(obj)" | sed -r 's/(OldUtility.getList\()[^)]*\)/\1)/'
    
    OldUtility.getList()
    

    我想我误读了参数提取的问题

    $ echo "OldUtility.getList(obj)" | sed -r 's/OldUtility(.getList\()([^)]*)\)/\2\1)/'
    
    obj.getList()
    

    最好从搜索模式中捕获字符串值以消除拼写错误并将值包含在一个位置。

    看来我又错过了一个。 这又处理了一个级别,但在没有前瞻的情况下 sed 处理起来会变得很复杂。

    $ echo "OldUtility.getList(otherObj.toObj())" | 
      sed -r 's/OldUtility(.getList\()([^)]+(\(\))?)/\2\1/'
    
    otherObj.toObj().getList()
    

    【讨论】:

    • 我认为你误解了 - 它需要导致obj.getList()
    猜你喜欢
    • 2018-03-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-10-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多