Сохранить часть совпадающего шаблона с переменной
Я хочу извлечь подстроку, соответствующую шаблону, и сохранить его в файл. Пример строки:
Apr 12 19:24:17 PC_NMG kernel: sd 11:0:0:0: [sdf] Attached SCSI removable disk
Я хочу извлечь часть между скобками, в этом случае [sdf]
.
Я попытался сделать что-то вроде grep -e '[$subtext]'
, чтобы сохранить текст в скобках в переменной. Конечно, это не работает, но я ищу способ, подобный этому. Было бы очень элегантно включать переменную в регулярное выражение, подобное этому. Что я могу сделать лучше всего?
Спасибо!
Ответы
Ответ 1
Вероятно, лучший способ использовать только 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'
Ответ 2
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[^]]+)\]')
Этот подход не вызывает внешних вызовов в цикле - поэтому для запуска внешних программ, таких как sed
или grep
, оболочке не требуется fork
и exec
. Таким образом, он, возможно, значительно чище, чем другие предлагаемые здесь методы.
Кстати, ваш первоначальный подход (с использованием grep) был далеко не таким; с помощью grep -o
выводится только соответствующая подстрока:
$ subtext=$(egrep -o "\[[^]]*\]" <<<"$line")
... хотя это включает скобки внутри захвата и, следовательно, не на 100% правильнее.
Ответ 3
Совпадение с регулярным выражением, замена с помощью группировки и печать только при регулярном выражении:
sed -n "s/.*\[\(.*\)\].*/\1/p"
Ответ 4
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