2016-12-14 3 views
7

J'ai un motif regex qui est censé correspondre à plusieurs endroits dans une chaîne. Je veux obtenir tous les groupes de correspondance dans un tableau et ensuite imprimer chaque élément.Bash regex ungreedy match

Alors, je l'ai essayé ceci:

#!/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 

Résultat:

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

Le résultat attendu aurait été

New match: Share1 
New match: Share2 

Je pense que cela ne fonctionne pas parce que mon .+? correspond à gourmand. J'ai donc cherché comment cela pouvait être accompli avec bash regex. Mais tout le monde semble suggérer d'utiliser grep avec perl regex.

Mais il doit sûrement y avoir un autre moyen. Je pensais peut-être quelque chose comme [^\\s]+ .. Mais la sortie de c'était:

New match: 
    Share1 Disk 
New match: Share1 

... Toutes les idées?

+0

Une idée serait d'utiliser '[^ \\ s] +' la place? de '. +?'. Cela correspondra aux caractères jusqu'à ce qu'un espace soit trouvé. – Rahul

+0

@Rahul ou '\ S +?' –

+0

Les deux produisent le même résultat que '[^ \\ s] +' que j'ai déjà mentionné dans ma question. Je ne pense pas que le '?' Est même supporté dans bash, je veux dire dans ce contexte .. Je veux dire un '?' Derrière un '+' signifie généralement 'match ungreedy'. – Forivin

Répondre

5

Il y a quelques problèmes ici. Tout d'abord, le premier élément de BASH_REMATCH correspond à la chaîne entière correspondant au modèle, et non au groupe de capture. Vous devez donc utiliser ${BASH_REMATCH[@]:1} pour obtenir les éléments qui figuraient dans les groupes de capture. Toutefois, bash regex ne prend pas en charge la répétition des correspondances plusieurs fois dans la chaîne, donc bash n'est probablement pas le bon outil pour ce travail. Puisque les choses sont sur leurs propres lignes bien, vous pouvez essayer d'utiliser pour diviser les choses et d'appliquer le modèle à chaque ligne comme:

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"