Utilisation de la commande grep avec un tableau de mots
J'ai un fichier texte avec quelques lignes et j'ai enregistré le fichier texte dans un tableau en utilisant
readarray -t array < Textfile
Le fichier texte contient par exemple les éléments suivants:
123
456
789
Maintenant, j'aimerais utiliser la commande grep pour rechercher les résultats dans un autre fichier texte en utilisant le tableau, donc imprimez les lignes où "123", "456" ou "789" apparaissent. Pour tester le tableau, j'ai essayé de faire en sorte que grep recherche "123", "456" et "789" le même fichier texte et affiche les lignes correspondantes, si au moins une des trois séries de caractères apparaît.
j'ai essayé
grep "${array[*]}" Textfile
mais cela ne montre aucun résultat. Qu'est-ce que je fais mal?
Réponses
Tout d'abord, vous pouvez simplement utiliser le fichier lui-même. C'est beaucoup plus simple que d'essayer d'utiliser un tableau shell:
grep -f file1.txt file2.txt
Cela imprimera toutes les lignes file2.txtqui correspondent à toutes les lignes dans file1.txt.
Si vous devez utiliser un tableau pour une raison quelconque, les choses sont plus compliquées. Vous ne pouvez pas simplement le faire, grep "${array[*]}" Textfilecar "${array[*]}"il s'étendra à la liste des éléments du tableau séparés par un espace:
$ array=("foo" "bar" "baz") $ echo "${array[*]}"
foo bar baz
Ce qui signifie que votre grepcommande deviendrait:
grep 'foo bar baz' file
Et cela signifie "grep pour foo bar bazdans le fichier file". Ce que vous voulez faire est de grep pour foo, ou bar , ou baz . Cela peut être fait en utilisant l'option -E grepet en joignant les modèles que vous souhaitez rechercher avec |:
grep -E 'foo|bar|baz' file
Pour ce faire, vous devrez faire quelque chose de compliqué comme:
grep -E "$(printf '%s|' "${array[@]}" | sed 's/|$//')" file
Ou peut-être:
grep -E "$(export IFS="|"; echo "${array[*]}")" file
Dans l'ensemble, il est tout simplement meilleur, plus rapide et plus facile d'utiliser le fichier et d'oublier le tableau.
grep "${array[*]}" Textfile
Fonctionnera tant que vous définissez IFSune nouvelle ligne (ou tout ce qui commence par une nouvelle ligne) et que vous utilisez --ou -epour vous assurer que cela fonctionne toujours même si le premier élément commence par -.
"${array[*]}"dans les shells de type Korn, comme "$*"dans les shells POSIX, se développe la liste des éléments joints avec le premier caractère de $IFS. La valeur par défaut de $IFSest <SPC><TAB><NL>( <SPC><TAB><NL><NUL>in zsh), donc par défaut, vous obtiendrez l'élément joint avec des caractères SPC. Pour grep, vous auriez besoin que les différentes expressions régulières soient séparées par une nouvelle ligne pour grepfaire une boucle pour chacune d'entre elles.
IFS=$'\n'
grep -e "${array[*]}" file
Dans zsh, faire grep -e "${(pj:\n:)array}" file(explicitement javec une nouvelle ligne au lieu de modifier $IFSglobalement), ou grep -e$^array file(qui étend le tableau dans fish/ rc-style, où cela devient grep -efirst -esecond fileune autre façon de fournir plusieurs modèles grep) serait plus propre.
Une autre option est de faire:
printf '%s\n' "${array[@]}" | grep -f - file
Cette fois, en passant la liste de motifs délimités par une nouvelle ligne sur greple stdin plutôt que via un argument.
Ces approches fonctionneraient que vos modèles soient des chaînes fixes (avec -F), des expressions régulières étendues (avec -E) ou des expressions régulières de base (par défaut).
Vous voudrez peut-être vous assurer que la liste des modèles n'est pas vide:
(( ${#array[@]} > 0 )) && grep ...
L'appel grepavec un modèle vide donne des résultats différents selon l' grepimplémentation et généralement pas ce que vous voulez.