Ответы:
Ключом к тому, чтобы заставить это работать, является sed
указание исключить то, что вы не хотите выводить, а также указать, что вы хотите.
string='This is a sample 123 text and some 987 numbers'
echo "$string" | sed -rn 's/[^[:digit:]]*([[:digit:]]+)[^[:digit:]]+([[:digit:]]+)[^[:digit:]]*/\1 \2/p'
Это говорит:
-n
)p
)В общем, sed
вы захватываете группы, используя скобки, и выводите то, что вы захватываете, используя обратную ссылку:
echo "foobarbaz" | sed 's/^foo\(.*\)baz$/\1/'
выведет "бар". Если вы используете -r
( -E
для OS X) для расширенного регулярного выражения, вам не нужно экранировать скобки:
echo "foobarbaz" | sed -r 's/^foo(.*)baz$/\1/'
Может быть до 9 групп захвата и их обратных ссылок. Обратные ссылки нумеруются в порядке появления групп, но их можно использовать в любом порядке и повторять:
echo "foobarbaz" | sed -r 's/^foo(.*)b(.)z$/\2 \1 \2/'
выводит "бар а".
Если у вас есть GNU grep
(он также может работать в BSD, включая OS X):
echo "$string" | grep -Po '\d+'
или варианты, такие как:
echo "$string" | grep -Po '(?<=\D )(\d+)'
-P
Опция позволяет Perl Compatible Regular Expressions. Смотрите man 3 pcrepattern
или man
3 pcresyntax
.
sed
пример, если вы используете -r
опцию (или -E
для OS X, IIRC), вам не нужно избегать скобок. Разница заключается в том, что между базовыми регулярными выражениями и расширенными регулярными выражениями ( -r
).
Sed имеет до девяти запоминаемых шаблонов, но вам нужно использовать экранированные скобки, чтобы запомнить части регулярного выражения.
Смотрите здесь для примеров и более подробно
sed -e 's/version=\(.+\)/\1/' input.txt
это все равно будет выводить весь input.txt
\+
вместо +
. И я не понимаю, почему люди используют -e
только одну команду sed.
sed -e -n 's/version=\(.+\)/\1/p' input.txt
см: mikeplate.com/2012/05/09/...
sed -E
использовать так называемые «современные» или «расширенные» регулярные выражения, которые намного ближе к Perl / Java / JavaScript / Go / любым другим разновидностям. (Сравните с grep -E
или egrep
.) Синтаксис по умолчанию имеет те странные экранирующие правила и считается "устаревшим". Для получения дополнительной информации о различиях между ними, запустите man 7 re_format
.
вы можете использовать grep
grep -Eow "[0-9]+" file
o
опция есть - unixhelp.ed.ac.uk/CGI/man-cgi?grep : -o, --only-Match Показать только часть совпадающей строки, которая соответствует PATTERN
grep -Eow -e "[0-9]+" -e "[abc]{2,3}"
я не знаю, как вы могли бы требовать, чтобы эти два выражения находились на одной строке, помимо конвейера из предыдущего grep (который все равно может не работать, если какой-либо шаблон соответствует более одного раза в строке ).
Этот ответ работает с любым количеством цифр групп. Пример:
$ echo 'Num123that456are7899900contained0018166intext' |
> sed -En 's/[^0-9]*([0-9]{1,})[^0-9]*/\1 /gp'
123 456 7899900 0018166
Есть ли способ сказать, что sed выводит только захваченные группы?
Да. замените весь текст группой захвата:
$ echo 'Number 123 inside text' | sed 's/[^0-9]*\([0-9]\{1,\}\)[^0-9]*/\1/'
123
s/[^0-9]* # several non-digits
\([0-9]\{1,\}\) # followed by one or more digits
[^0-9]* # and followed by more non-digits.
/\1/ # gets replaced only by the digits.
Или с расширенным синтаксисом (меньше обратных кавычек и допускается использование +):
$ echo 'Number 123 in text' | sed -E 's/[^0-9]*([0-9]+)[^0-9]*/\1/'
123
Чтобы избежать печати оригинального текста, когда номера нет, используйте:
$ echo 'Number xxx in text' | sed -En 's/[^0-9]*([0-9]+)[^0-9]*/\1/p'
И сопоставить несколько чисел (а также распечатать их):
$ echo 'N 123 in 456 text' | sed -En 's/[^0-9]*([0-9]+)[^0-9]*/\1 /gp'
123 456
Это работает для любого количества запусков цифр:
$ str='Test Num(s) 123 456 7899900 contained as0018166df in text'
$ echo "$str" | sed -En 's/[^0-9]*([0-9]{1,})[^0-9]*/\1 /gp'
123 456 7899900 0018166
Что очень похоже на команду grep:
$ str='Test Num(s) 123 456 7899900 contained as0018166df in text'
$ echo "$str" | grep -Po '\d+'
123
456
7899900
0018166
и шаблон:
/([\d]+)/
Sed не распознает синтаксис «\ d» (ярлык). Используемый выше эквивалент ascii [0-9]
не совсем эквивалентен. Единственное альтернативное решение - использовать класс символов: '[[: digit:]] `.
Выбранный ответ использует такие «классы символов» для построения решения:
$ str='This is a sample 123 text and some 987 numbers'
$ echo "$str" | sed -rn 's/[^[:digit:]]*([[:digit:]]+)[^[:digit:]]+([[:digit:]]+)[^[:digit:]]*/\1 \2/p'
Это решение работает только для (точно) двух серий цифр.
Конечно, поскольку ответ выполняется внутри оболочки, мы можем определить пару переменных, чтобы сделать такой ответ короче:
$ str='This is a sample 123 text and some 987 numbers'
$ d=[[:digit:]] D=[^[:digit:]]
$ echo "$str" | sed -rn "s/$D*($d+)$D+($d+)$D*/\1 \2/p"
Но, как уже было объяснено, s/…/…/gp
лучше использовать команду:
$ str='This is 75577 a sam33ple 123 text and some 987 numbers'
$ d=[[:digit:]] D=[^[:digit:]]
$ echo "$str" | sed -rn "s/$D*($d+)$D*/\1 /gp"
75577 33 123 987
Это будет охватывать как повторные серии цифр, так и написание короткой (er) команды.
Я полагаю, что шаблон, приведенный в вопросе, был только в качестве примера, и цель состояла в том, чтобы соответствовать любому шаблону.
Если у вас есть sed с расширением GNU, позволяющим вставлять новую строку в пространство шаблонов, одно из предложений:
> set string = "This is a sample 123 text and some 987 numbers"
>
> set pattern = "[0-9][0-9]*"
> echo $string | sed "s/$pattern/\n&\n/g" | sed -n "/$pattern/p"
123
987
> set pattern = "[a-z][a-z]*"
> echo $string | sed "s/$pattern/\n&\n/g" | sed -n "/$pattern/p"
his
is
a
sample
text
and
some
numbers
Эти примеры с tcsh (да, я знаю, что это не та оболочка) с CYGWIN. (Правка: для bash удалите set и пробелы вокруг =.)
+
, вам нужно его отключить или использовать -r
опцию ( -E
для OS X). Вы также можете использовать \{1,\}
( -r
или -E
без побега).
Сдайся и используй Perl
Так sed
как не обрезает его, давайте просто выбросим полотенце и используем Perl, по крайней мере это LSB, а grep
расширения GNU - нет :-)
Напечатайте всю соответствующую часть, не требуя соответствующих групп или вида сзади:
cat <<EOS | perl -lane 'print m/\d+/g'
a1 b2
a34 b56
EOS
Вывод:
12
3456
Одно совпадение на строку, часто структурированные поля данных:
cat <<EOS | perl -lape 's/.*?a(\d+).*/$1/g'
a1 b2
a34 b56
EOS
Вывод:
1
34
Сзади
cat <<EOS | perl -lane 'print m/(?<=a)(\d+)/'
a1 b2
a34 b56
EOS
Несколько полей:
cat <<EOS | perl -lape 's/.*?a(\d+).*?b(\d+).*/$1 $2/g'
a1 c0 b2 c0
a34 c0 b56 c0
EOS
Вывод:
1 2
34 56
Несколько совпадений в строке, часто неструктурированные данные:
cat <<EOS | perl -lape 's/.*?a(\d+)|.*/$1 /g'
a1 b2
a34 b56 a78 b90
EOS
Вывод:
1
34 78
Сзади
cat EOS<< | perl -lane 'print m/(?<=a)(\d+)/g'
a1 b2
a34 b56 a78 b90
EOS
Вывод:
1
3478
Пытаться
sed -n -e "/[0-9]/s/^[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\).*$/\1 \2 \3 \4 \5 \6 \7 \8 \9/p"
Я получил это под Cygwin:
$ (echo "asdf"; \
echo "1234"; \
echo "asdf1234adsf1234asdf"; \
echo "1m2m3m4m5m6m7m8m9m0m1m2m3m4m5m6m7m8m9") | \
sed -n -e "/[0-9]/s/^[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\).*$/\1 \2 \3 \4 \5 \6 \7 \8 \9/p"
1234
1234 1234
1 2 3 4 5 6 7 8 9
$
Это не то, о чем просил ФП (захват групп), но вы можете извлечь числа, используя:
S='This is a sample 123 text and some 987 numbers'
echo "$S" | sed 's/ /\n/g' | sed -r '/([0-9]+)/ !d'
Дает следующее:
123
987
sed
включить расширенные регулярные выражения с-E
флагом.