Сохраните часть соответствия шаблону к переменной

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

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

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

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

Спасибо!

12
задан Charles Duffy 13 April 2010 в 01:08
поделиться

4 ответа

Возможно, лучший способ использовать только 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'
10
ответ дан 2 December 2019 в 05:53
поделиться

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% правильно.

12
ответ дан 2 December 2019 в 05:53
поделиться

Сопоставить с regex, заменить с помощью группировки и печатать только если regex совпал:

sed -n "s/.*\[\(.*\)\].*/\1/p"
4
ответ дан 2 December 2019 в 05:53
поделиться

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
0
ответ дан 2 December 2019 в 05:53
поделиться
Другие вопросы по тегам:

Похожие вопросы: