Регулярное выражение в Linux команда sed - PullRequest
0 голосов
/ 22 февраля 2012

У меня есть переменная оболочки:

all_apk_file="a 1 2.apk x.apk y m.apk"

Я хочу заменить a 1 2.apk на TEST, используя команду:

echo $all_apk_file | sed 's/(.*apk ){1}/TEST/g'

.*apk означает конецс apk, {1} означает совпадение только один раз, но это не работает;Я получил только исходную переменную в качестве вывода: a 1 2.apk x.apk y m.apk

Может кто-нибудь сказать мне, почему?

Ответы [ 3 ]

2 голосов
/ 22 февраля 2012

Сначала , чтобы включить регулярные выражения, с которыми вы знакомы в sed, вам необходимо использовать переключатель -r (sed -r ...):

echo $all_apk_file | sed -r 's/(.*apk ){1}/TEST/g'
# returns TESTy m.apk

Посмотрите, что он возвращает: TESTy m.apk. Это потому, что .* является жадным , поэтому он соответствует настолько, насколько это возможно . То есть .* соответствует a 1 2.apk x, и вы сказали, что хотите заменить .*apk, то есть a 1 2.apk x.apk на 'TEST', что приведет к TESTy m.apk (обратите внимание на следующий пробел после '.apk' в вашем регулярном выражении, поэтому совпадение не распространяется на последний «.apk», который не имеет пробела после него).

Обычно можно изменить .* на .*?, чтобы сделать его нежадным , но это поведение не поддерживается в sed.

Итак, чтобы исправить это, вам просто нужно сделать свое регулярное выражение более строгим.

Трудно сказать, что вы хотите сделать - удалить первые три слова, где третье заканчивается на .apk, и заменить на «ТЕСТ»? В этом случае можно использовать регулярное выражение:

[a-z0-9]+ +[a-z0-9]+ +[a-z0-9]+\.apk

в сочетании с переключателем 'i' (без учета регистра).

Вы должны будете предоставить свою логику для решения, что удалять (первые три слова, любое количество слов до первого слова .apk и т. Д.), Чтобы мы могли помочь вам в дальнейшем с регулярным выражением.

Во-вторых , вы вставили переключатель 'g' в свое регулярное выражение. Это означает, что все совпадающие шаблоны будут заменены, и вы, кажется, хотите, чтобы только первая была заменена. Так что уберите переключатель «g».

Наконец , все это в комбинации:

echo $all_apk_file | sed -r 's/[a-z0-9]+ +[a-z0-9]+ +[a-z0-9]+\.apk/TEST/i'
# TEST x.apk y m.apk
1 голос
/ 22 февраля 2012

Это может работать для вас:

echo "$all_apk_file" | sed 's/apk/\n/;s/.*\n/TEST/'
TEST x.apk y m.apk

Относительно того, почему ваше регулярное выражение не сработало, смотрите превосходные объяснения @ математика.coffee и @Jonathan Leffler.

s/apk/\n/ является синонимом s/apk/\n/1, что означает замену первого вхождения apk на \n. Поскольку sed использует \n в качестве разделителя записей, мы знаем, что это не может произойти ни в одной начальной строке, передаваемой командам sed. С этими двумя фактами мы можем разделить строки.

N.B. Если вы хотите заменить до второго apk, то s/apk/\n/2 будет соответствовать всем требованиям. Конечно, в последний раз apk тогда в игру вступает .*apk.

0 голосов
/ 22 февраля 2012

Одна часть проблемы заключается в том, что в обычных sed () и {} являются обычными символами в шаблонах до тех пор, пока их не уберут с обратной косой чертой.Поскольку в значении переменной нет скобок, регулярное выражение никогда не совпадает.В GNU sed вы также можете включить расширенные регулярные выражения с флагом -r.Если вы исправите эту проблему, вы столкнетесь с проблемой жадности .*, и модификатор g фактически ничего не изменит:

$ echo $all_apk_file | sed 's/\(.*apk \)\{1\}/TEST/g'
TESTy m.apk
$ echo $all_apk_file | sed -r 's/(.*apk ){1}/TEST/g'
TESTy m.apk
$ echo $all_apk_file | sed -r 's/(.*apk ){1}/TEST/'
TESTy m.apk
$

Он останавливается только потому, что нет '• пробел после m.apk в отображаемом значении переменной.

Теперь возникает вопрос: что вы хотите заменить?Это звучит как «все до и включая первое вхождение apk в конце слова».Вероятно, это легче всего сделать с помощью конечного контекста или не жадного сопоставления, как в регулярных выражениях PerlЕсли вы хотите перейти на Perl, сделайте это.Если нет, то это не тривиально в обычных sed регулярных выражениях.

$ echo $all_apk_file | sed 's/^[^.]* [^.][^.]*\.apk /TEST /'
TEST x.apk y m.apk
$

Это ищет что-либо без точек, сопровождаемое пробелом, затем снова без точек, и .apk;это означает, что первая разрешенная точка - это точка в 2.apk.Это работает для данных образца;это не будет работать, если переменная содержит:

all_apk_file="a 1.2 2.apk m.apk y.apk 37"

Вам необходимо настроить это, чтобы удовлетворить ваши требования.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...