2017-07-27 4 views
0

J'ai un script bash qui comprend une ligne comme celui-ci:substitution de commandes bash bloque le script (sortie trop longtemps?) - Comment faire face

matches="`grep --no-filename $searchText $files`" 

En d'autres termes, j'assigne le résultat d'un grep à une variable.

J'ai récemment trouvé que cette ligne de code semble avoir une vulnérabilité: si le grep trouve trop de résultats, cela gêne simplement l'exécution. Premièrement, si quelqu'un peut confirmer qu'une sortie excessive (et exactement ce qui constitue excessif) est un danger connu avec substitution de commande, s'il vous plaît fournir un lien solide pour moi. Je recherche sur le Web, et la référence la plus proche que je pourrais trouver est dans this link:

"Ne pas définir une variable pour le contenu d'un long fichier texte, sauf si vous avez une très bonne raison de le faire."

Cela laisse entendre qu'il existe un danger, mais qu'il est très insuffisant. Deuxièmement, y a-t-il une meilleure pratique connue pour faire face à cela? Le comportement que je veux vraiment est une sortie excessive dans la substitution de commande pour générer un message d'erreur lisible par un humain suivi d'un code de sortie d'erreur afin que mon script se termine au lieu de geler. (Note: Je cours toujours mes scripts avec "set -e" comme l'une des lignes initiales). Est-il possible que je peux obtenir ce comportement?

Actuellement, la seule solution que je connaisse est un hack qui sorta fonctionne juste pour mon cas immédiat: je peux limiter la sortie de grep en utilisant son option --max-count.

+0

"Gèle l'exécution"? Pas vraiment. Il pourrait manquer et mémoire et échouer (qui ne gèle pas), ou il pourrait passer en swap (ce qui pourrait ralentir le système * a * beaucoup *) si vous ne disposez pas d'ulimits pour limiter de manière appropriée l'allocation de mémoire par processus , mais il n'y a pas un état qui est juste gelé. –

+0

BTW, en utilisant réflexivement 'set -e' [n'est pas forcément une bonne idée] (http://mywiki.wooledge.org/BashFAQ/105). (Ignorez l'allégorie si c'est trop à lire, mais * ne * voyez pas combien de questions du quiz ci-dessous vous pouvez décider si vous comprenez le comportement de 'set -e' assez bien pour l'utiliser en toute sécurité). –

+0

En outre, '$ files' comme expansion non-cotée est une odeur de code - indique que votre code ne fonctionnera certainement pas correctement en présence de noms de fichiers avec des espaces, par exemple, et peut aussi surprendre lorsque les noms contiennent glob métacaractères. La meilleure pratique consiste à stocker des listes dans un tableau. par exemple: 'files = (* .txt)', puis dereferencing '" $ {files [@]} "' –

Répondre

1

Idéalement, vous ne devriez pas capturer de données de longueur inconnue dans la mémoire ; Si vous le lisez comme vous en avez besoin, alors grep attendra jusqu'à ce que le contenu soit prêt à être utilisé.

C'est:

while IFS= read -r match; do 
    echo "Found a match: $match" 
    # example: maybe we want to look at whether a match exists on the filesystem 
    [[ -e $match ]] && { echo "Got what we needed!" >&2; break; } 
done < <(grep --no-filename "$searchText" "${files[@]}") 

De cette façon, grep écrit seulement une ligne quand read est prêt à consommer (et bloquera au lieu d'avoir besoin de continuer à lire l'entrée si elle a plus de sortie déjà produit que peut être stocké dans le tampon de pipe relativement petit) - ainsi les noms dont vous n'avez pas besoin ne sont même pas générés en premier lieu, et il n'est pas nécessaire d'allouer de la mémoire ou de les traiter d'une autre manière.

+0

c'est une excellente solution, merci! – HaroldFinch

+0

Heh. Si j'avais su que vous étiez sur cygwin, je n'aurais peut-être pas suggéré de substitution de processus (la syntaxe '<()') - il y avait un bug de longue date qui l'a influencé - mais ça s'est avéré en amont : //cygwin.com/ml/cygwin/2013-04/msg00120.html). –