2016-12-14 6 views
7

У меня есть шаблон регулярного выражения, который должен совпадать в нескольких местах в строке. Я хочу, чтобы все группы соответствия были в одном массиве, а затем печатали каждый элемент.Bash regex ungreedy match

Итак, я пытался это:

#!/bin/bash 

f=$'\n\tShare1 Disk\n\tShare2 Disk\n\tPrnt1 Printer' 
regex=$'\n\t(.+?)\\s+Disk' 
if [[ $f =~ $regex ]] 
then 
    for match in "${BASH_REMATCH[@]}" 
    do 
     echo "New match: $match" 
    done 
else 
    echo "No matches" 
fi 

Результат:

New match: 
    Share1 Disk 
    Share2 Disk 
New match: Share1 Disk 
    Share2 

Ожидаемый результат был бы

New match: Share1 
New match: Share2 

Я думаю, что это не работает, потому что мой .+? соответствует жадным. Поэтому я посмотрел, как это можно сделать с помощью rexx bash. Но все, кажется, предлагают использовать grep с регулярным выражением perl.

Но, безусловно, должен быть другой способ. Я думал, может быть что-то вроде [^\\s]+ .. Но выход для этого был:

New match: 
    Share1 Disk 
New match: Share1 

... Любые идеи?

+0

одна идея будет использовать '[^ \\ s] +' вместо этого? '. +?'. Это будет соответствовать символам до пробела, если будет найден. – Rahul

+0

@Rahul или '\ S +?' –

+0

Оба дают тот же результат, что и '[^ \\ s] +', о котором я уже упоминал в моем вопросе. Я не думаю, что '?' Поддерживается даже в bash, я имею в виду в этом контексте. Я имею в виду, что '' 'за' + 'обычно означает' match ungreedy'. – Forivin

ответ

5

Здесь есть пара вопросов. Во-первых, первый элемент BASH_REMATCH - это целая строка, которая соответствует шаблону, а не группе захвата, поэтому вы хотите использовать ${BASH_REMATCH[@]:1}, чтобы получить те вещи, которые были в группах захвата.

Однако, bash regex не поддерживает повторение совпадений несколько раз в строке, поэтому bash, вероятно, не подходит для этого задания. Так как вещи на своих линиях, хотя, вы могли бы попытаться использовать, чтобы разделить вещи и применить шаблон к каждой строке, например:

f=$'\n\tShare1 Disk\n\tShare2 Disk\n\tPrnt1 Printer' 
regex=$'\t(\S+?)\\s+Disk' 
while IFS=$'\n' read -r line; do 
    if [[ $line =~ $regex ]] 
    then 
     printf 'New match: %s\n' "${BASH_REMATCH[@]:1}" 
    else 
     echo "No matches" 
    fi 
done <<<"$f"