печать совпавших шаблонов с использованием команд Unix - PullRequest
1 голос
/ 03 февраля 2012

Ко мне пришел сотрудник с проблемой, которая, как мне показалось, будет тривиальной, но оказалась сложной. Сложность заключается в следующем: с учетом входной строки, какие команды Unix можно использовать для распечатки любых соответствующих шаблонов в строке?

Допустим, у нас есть следующая строка ввода.

12345 4444 abc 789012 xyz 1234567 def 987654321 qrz 60606

Цель состоит в том, чтобы распечатать любые 5-значные или 6-значные числа в строке, но не любые 4-значные или 7-значные числа.

Сначала мы подумали об использовании sed следующим образом:

echo "12345 4444 abc 789012 xyz 1234567 def 987654321 qrz 60606" |
  sed 's/.*[^0-9]\{1\}\([0-9]\{5,6\}\).*/\1/g'

Эта команда, однако, печатает только последнее вхождение любых подходящих шаблонов.

Мы наконец-то придумали использовать комбинацию sed и grep -Eo.

echo "12345 4444 abc 789012 xyz 1234567 def 987654321 qrz 60606" |
  sed 's/^/ /' | sed 's/$/ /' | grep -Eo '[[:space:]]+[0-9]{5,6}[[:space:]]+' |
  sed 's/ $//' | sed 's/^ //'

Это работает, но кажется немного глупым. Есть ли лучший способ?

Ответы [ 6 ]

3 голосов
/ 03 февраля 2012

GNU grep, по крайней мере, поддерживает -o, и я думаю, что это также в версии POSIX.

-o, --only-Match Показать только часть совпадающей строкисоответствует PATTERN.

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

Как насчет tr и grep?

echo "12345 4444 abc 789012 xyz 1234567 def 987654321 qrz 60606" |
  tr ' ' '\n' |
  grep '^[[:digit:]]\{5,6\}$'

Или, как рекомендует dmckee, вы можете использовать флаг -o, чтобы grep пропустить этап tr (если ваша версия grep имеет этот флаг):

echo "12345 4444 abc 789012 xyz 1234567 def 987654321 qrz 60606" |
  grep -o '\<[[:digit:]]\{5,6\}\>'
0 голосов
/ 17 апреля 2014

Фильтрует каждую строку, выводит только 5 или 6-значные слова, но сохраняет их в одной строке

 perl -ne 'print join(" ",grep /\b[0-9]{5,6}\b/, split)."\n";'

Если ввод

12345 4444 abc 789012 xyz 1234567 def 987654321 qrz 60606
hello
66666

Вывод

12345 789012 60606

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

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

echo "12345 4444 abc 789012 xyz 1234567 def 987654321 qrz 60606" |
sed 's/\<[0-9]\{,4\}\>//g;s/\<[0-9]\{7,\}\>//g;s/[^0-9]\+/ /g'
12345 789012 60606

Или:

echo "12345 4444 abc 789012 xyz 1234567 def 987654321 qrz 60606" |
sed 'H;g;:a;s/\n\([0-9]\{5,6\}\)\> */\1 \n/;ta;s/\n[^ ]* /\n/;ta;s/..$//'
12345 789012 60606
0 голосов
/ 03 февраля 2012

Если вы хотите, вы можете сделать это, используя только встроенные средства Bash, без внешних утилит, таких как tr или sed или grep:

INPUT='12345 4444 abc 789012 xyz 1234567 def 987654321 qrz 60606'
( set -f
  for word in $INPUT ; do
    if [[ $word =~ ^[0-9]{5,6}$ ]] ; then
      echo $word
    fi
  done
)

(set -f - отключить расширение имени файла, чтобы мы могли разделить $INPUT на составляющие его слова, не беспокоясь о том, что он может содержать * или что-то, что может расшириться до списка имен файлов. ( ... ) должен содержать эффект set -f, поэтому нам не нужно беспокоиться о том, действительно ли окружающий контекст хочет, чтобы расширение имени файла было отключено.)

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

работал на linux box

echo "12345 4444 abc 789012 xyz 1234567 def 987654321 qrz 60606"|egrep -o 's|[[:digit:]]{5,6} |p'
...