группы захвата sed не работают


27

У меня есть строка в формате [0-9]+\.[0-9]+\.[0-9]. Мне нужно извлечь первое, второе и третье числа отдельно. Насколько я понимаю, группы захвата должны быть способны на это. Я должен быть в состоянии использовать, sed "s/\([0-9]*\)/\1/gчтобы получить первый номер, sed "s/\([0-9]*\)/\2/gполучить второй номер и sed "s/\([0-9]*\)/\3/gполучить третий номер. В каждом случае, однако, я получаю всю строку. Почему это происходит?


6
Группы захвата захватывают всю группу ... не отдельные элементы в группе. Вам нужно что-то вроде 's/\([0-9]\)\([0-9]\)\([0-9]\).*/\1\2\3/'захвата отдельных номеров.
Мунир

Ответы:


45

Мы не можем дать вам полный ответ без примера вашего вклада, но я могу вам сказать, что ваше понимание групп захвата неверно. Вы не используете их последовательно, они относятся только к регулярному выражению в левой части того же оператора замещения. Если вы захватите, например, /(foo)(bar)(baz)/то fooбудет \1, barбудет \2и bazбудет \3. Вы не можете сделать это s/(foo)/\1/; s/(bar)/\2/, потому что во втором s///вызове есть только одна захваченная группа, поэтому \2она не будет определена.

Итак, чтобы получить три группы цифр, вам нужно сделать:

sed 's/\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\)/\1 : \2 : \3/'

Или, тем более читаемым:

sed -E 's/([0-9]*)\.([0-9]*)\.([0-9]*)/\1 : \2 : \3/'

1
Какая польза от скобок в первом примере?
Джош М.

2
@JoshM. вам нужно избежать их, чтобы они могли использоваться для захвата паттернов. Обычно /(foo)/в Sed будет соответствовать буквальный (символ, fooа затем и буквальный ). Если вы хотите захватить группу, вам нужно либо убрать скобки, либо использовать эту -Eопцию.
Тердон

Я почти всегда использую -rфлаг, поэтому я предполагаю, что поэтому я еще не сталкивался с этим.
Джош М.

1
@JoshM. да, -rфлаг тоже это сделает, но он не переносимый. GNU sed поддерживает это, но многие другие этого не делают. Это -Eболее универсально.
Тердон

9

Пример:

$ echo "123.456.78" |sed 's/\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\)/\1/'
123

$ echo "123.456.78" |sed 's/\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\)/\2/'
456

$ echo "123.456.78" |sed 's/\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\)/\3/'
78

Или все вместе:

$ echo "123.456.78" |sed 's/\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\)/\1 : \2 : \3/'
123 : 456 : 78

2

Используйте Sed с -r, --regexp-extended, чтобы избежать всех экранированных скобок.

echo "1234.567.89" | sed -r 's/([0-9]+)\.([0-9]+)\.([0-9]+)/\1, \2, \3/' 
1234, 567, 89    #output
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.