Utilisation de la commande grep avec un tableau de mots

Sep 10 2020

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

4 terdon Sep 10 2020 at 13:43

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.

StéphaneChazelas Sep 10 2020 at 17:18
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.