Сохранить часть соответствующего шаблона в переменную - PullRequest
11 голосов
/ 12 апреля 2010

Я хочу извлечь подстроку, соответствующую шаблону, и сохранить ее в файл. Пример строки:

Apr 12 19:24:17 PC_NMG kernel: sd 11:0:0:0: [sdf] Attached SCSI removable disk

Я хочу извлечь часть в скобках, в данном случае [sdf].

Я пытался сделать что-то вроде grep -e '[$subtext]', чтобы сохранить текст в скобках в переменной. Конечно, это не работает, но я ищу способ, похожий на этот. Было бы очень элегантно включить переменную в регулярное выражение, как это. Что я могу сделать лучше всего?

Спасибо!

Ответы [ 4 ]

11 голосов
/ 13 апреля 2010

BASH_REMATCH - это массив, содержащий группы, соответствующие оболочке.

$ line='Apr 12 19:24:17 PC_NMG kernel: sd 11:0:0:0: [sdf] Attached SCSI removable disk'
$ [[ $line =~ \[([^]]+)\] ]]; echo "${BASH_REMATCH[1]}"
sdf

Если вы хотите поместить это в цикл, вы можете сделать это; Вот пример:

while read -r line; do
  if [[ $line =~ \[([^]]+)\] ]] ; then
    drive="${BASH_REMATCH[1]}"
    do_something_with "$drive"
  fi
done < <(dmesg | egrep '\[([hsv]d[^]]+)\]')

Этот подход не помещает внешние вызовы в цикл - поэтому оболочке не нужно fork и exec для запуска внешних программ, таких как sed или grep. Таким образом, он, возможно, значительно чище, чем другие подходы, предлагаемые здесь.

Кстати, ваш первоначальный подход (с использованием grep) был не так уж далек; использование grep -o выведет только соответствующую подстроку:

$ subtext=$(egrep -o "\[[^]]*\]" <<<"$line")

... хотя это включает в себя скобки внутри захвата и, следовательно, не на 100% правильно.

9 голосов
/ 12 апреля 2010

Возможно, есть лучший способ использовать только bash, но:

echo 'Apr 12 19:24:17 PC_NMG kernel: sd 11:0:0:0: [sdf] Attached SCSI removable disk' \
| sed -s 's/.*\[\(.*\)\].*/\1/'

Как указывает Юрген, это соответствует несовпадающим строкам. Если вы не хотите выводить несоответствующие строки, используйте '-n', чтобы не выводить шаблон, и '/ p', чтобы вывести шаблон, когда он совпадает.

| sed -n 's/.*\[\(.*\)\].*/\1/p'
4 голосов
/ 12 апреля 2010

Сравнение с регулярным выражением, замена с использованием группировки и печать только в том случае, если регулярное выражение соответствует:

sed -n "s/.*\[\(.*\)\].*/\1/p"
0 голосов
/ 13 апреля 2010

sed является жадным, поэтому ответы sed пропустят некоторые данные, если в ваших данных будет больше пар []. Используйте решение grep + tr или вы можете использовать awk

$ cat file
[sss]Apr 12 19:24:17 PC_NMG kernel: sd 11:0:0:0: [sdf] Attached SCSI removable disk [tag] blah blah

$ awk -F"[" '{for(i=2;i<=NF;i++){if($i~/\]/){sub("].*","",$i)};print $i}}' file
sss
sdf
tag
...