как извлечь слово из результата grep в оболочке?

#shell

#оболочка

Вопрос:

Используя оболочку, я хочу искать и печатать только вложенную строку со следующим словом к этой вложенной строке.

например, в файле журнала есть строка «сегодня понедельник, и это: 1234, так что я нахожусь».

 if grep -q "this is :" ./logfile; then
   #here i want to print only sub-string with next word i.e. "this is:1234"
   #echo ???
fi
  

Комментарии:

1. Какой длины файл? Если он короткий, может быть быстрее выполнить этот поиск в собственной оболочке, а не создавать внешние инструменты ( grep , sed , awk , и т. Д.), Хотя эти инструменты, Как правило, будут обрабатывать содержимое быстрее, как только стоимость их запуска будет оплачена.

2. Кстати, внешний if grep -q , возможно, плохая идея — означает, что вы читаете файл дважды, один раз, чтобы проверить его содержимое, а затем снова, чтобы прочитать его содержимое. Если вы собираетесь запустить внешнюю команду, лучше использовать if result=$(that-command); then ... как для захвата вывода, так и для проверки состояния выхода за один проход. Если команда не устанавливает статус выхода в зависимости от того, есть ли совпадение, это if result=$(that-command) amp;amp; [ -n "$result" ]; then может иметь больше смысла.

Ответ №1:

Вы можете использовать sed with 1 для отображения соответствующей строки в (..) :

 sed 's/.*(this is:[0-9a-zA-Z]*).*/1/' logfile
  

РЕДАКТИРОВАТЬ: приведенная выше команда подходит только для ввода в 1 строку.

Когда у вас есть файл с большим количеством строк, вы хотите печатать только те строки, которые совпадают:

 sed -n 's/.*(this is:[0-9a-zA-Z]*).*/1/p' logfile
  

Если у вас большой файл и вы хотите увидеть только первое совпадение, вы можете объединить эту команду с head -1 , но вы хотели бы остановить сканирование / синтаксический анализ после первого совпадения. Вы можете использовать q для выхода, но вы хотите выйти только после совпадения.

 sed -n '/.*(this is:[0-9a-zA-Z]*).*/{s//1/p;q}'
  

Комментарии:

1. Если бы вы изменили это, чтобы также исключить все остальные строки и выйти после нахождения совпадения, я бы подумал, что мы были бы настроены.

Ответ №2:

Вы можете использовать регулярное выражение с предварительным просмотром, если вам нужно только следующее слово:

 $ grep --perl-regexp -o '(?<=(this is:))(S )' ./logfile
1234
  

Если вы хотите оба, то просто:

 $ grep --perl-regexp -o 'this is:S ' ./logfile
this is:1234
  

-o Опция указывает grep возвращать только соответствующую часть.

В приведенных выше командах мы предполагали, что «слово» представляет собой последовательность символов, не содержащих пробелов. Вы можете настроить это в соответствии с вашими потребностями.

Ответ №3:

Если у вас есть система с расширениями GNU (но вы не уверены, что она была скомпилирована с дополнительной поддержкой PCRE), рассмотрите:

 if result=$(grep -E -m 1 -o 'this is:[^[:space:]] ' logfile); then
  echo "value is: ${result#*:}"
fi
  

${varname#value} расширяется до содержимого varname , но с value удалением с самого начала, если оно присутствует. Таким образом, ${result#*:} удаляет все до первого двоеточия result .

Однако это может не работать в системах без параметров, отличных от POSIX -o , или -m .


Если вы хотите поддерживать системы, отличные от GNU awk , стоит ли рассмотреть этот инструмент: в отличие от ответов, требующих непереносимых расширений (например grep -P ), это должно работать на любой современной платформе (протестировано с GNU awk, недавними BSD awk и mawk; кроме того, никаких предупреждений с with gawk --posix --lint ):

 # note that the constant 8 is the length of "this is:"
# GNU awk has cleaner syntax, but trying to be portable here.
if value=$(awk '
  BEGIN { matched=0; }      # by default, this will trigger END to exit as failure
  /this is:/ {
    match($0, /this is:([^[:space:]] )/);
    print substr($0, RSTART 8, RLENGTH-8);
    matched=1;              # tell END block to use zero exit status
    exit(0);                # stop processing remaining file contents, jump to END
  }
  END { if(matched == 0) { exit(1); } }
'); then
  echo "Found value of $value"
else
  echo "Could not find $value in file"
fi
  

Ответ №4:

Вы можете искать все вплоть до, но не включая следующий пробел, подобный этому:

 grep -Eo "this is:[^[:space:]] " logfile
  

[] Вводит набор символов, которые вы ищете, а ^ в начале дополняет набор, поэтому набор символов, который вы ищете, является пробелом, но дополняется, т. Е. Не пробелом. В нем говорится, что должен быть хотя бы один или несколько таких символов.

-E Указывает grep использовать расширенные регулярные выражения и -o средства для печати только соответствующей части.

Комментарии:

1. Я бы предложил использовать [^[:space:]] , чтобы также остановиться на символах табуляции и c.

2. @CharlesDuffy Да, это определенное улучшение — спасибо.