Команда sed для замены только того, что находится внутри соответствующего набора скобок

У меня есть старый код, похожий на 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(...)?


person Sean    schedule 05.04.2016    source источник


Ответы (3)


Поскольку 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'
person Casimir et Hippolyte    schedule 05.04.2016
comment
@Sean: как видите, глобальный синтаксис не сильно отличается от синтаксиса sed (например: s/.../.../x). Параметр p автоматически печатает строку. Главное понять рекурсию: вы открываете группу захвата, где подшаблон содержит ссылку на себя ((?1)). - person Casimir et Hippolyte; 05.04.2016
comment
Я знаю, что это требует многого — вы можете отказаться. Но вы бы сэкономили мне кучу времени... Мне также нужно иметь возможность реорганизовать еще один шаблон из OldUtility.addItem(obj, item) в obj.getList().addItem(item) со всеми теми же ограничениями на поддержку вложенных скобок. Я пытаюсь узнать все о рекурсии perl, чтобы принять ваш ответ, но я предполагаю, что вы могли бы сделать это быстрее - person Sean; 06.04.2016
comment
@Sean: Я прощаю тебя, потому что это сложнее. Из-за того, что запятая может встречаться только в рекурсии (внутри скобок), вам нужно использовать условное выражение, чтобы проверить, находитесь ли вы в рекурсии: (?(R)A|B) (если рекурсия, то A иначе B). Результат: s/OldUtility\.addItem\(((?(R)[^()]*+(?:\((?1)\)[^()]*)*+|[^(),]*+(?:\((?1)\)[^(),]*)*+)),\h*/$1.getList().addItem(/g демо: regex101.com/r/oK4nQ5/1 - person Casimir et Hippolyte; 06.04.2016
comment
Если бы я мог, я бы купил тебе пива. Большое спасибо! - person Sean; 06.04.2016
comment
@Шон: ((?(R)er|be))(?1) ? - person Casimir et Hippolyte; 06.04.2016

Если вы не хотите захватывать закрывающую скобку, используйте [^)]* вместо .*.

Протестировано с этим:

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'.

person Laurel    schedule 05.04.2016
comment
Спасибо - но это не совсем так. Если вы попробуете: $ echo OldUtility.getList(otherObj.toObj()) | sed 's/(OldUtility.getList()([^)]*))/\2.getList()/g' Вы получите: otherObj.toObj(.getList()) - person Sean; 05.04.2016
comment
Ты победитель! Спасибо. однако, обратите внимание - первая копия команды не совпадает со второй копией, и, похоже, в ней отсутствуют некоторые скобки. Но вторая копия работает отлично! - person Sean; 05.04.2016
comment
Фактически второй является копией первого. OldUtility.getList. на самом деле должно быть OldUtility\.getList\(, но это имеет значение только в крайних случаях. - person Laurel; 05.04.2016

вы делаете это более сложным, чем нужно.

$ 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()
person karakfa    schedule 05.04.2016
comment
Я думаю, вы неправильно поняли - это должно привести к obj.getList() - person Sean; 05.04.2016