408 votes

Comment tester si une chaîne existe dans un fichier avec Bash ?

J'ai un fichier qui contient les noms des répertoires :

my_list.txt :

/tmp
/var/tmp

J'aimerais vérifier dans Bash avant d'ajouter un nom de répertoire si ce nom existe déjà dans le fichier.

0 votes

Pour trouver toutes les chaînes de caractères d'un fichier, vous pouvez exécuter grep dans une boucle FOR : unix.stackexchange.com/a/462445/43233

779voto

Thomas Points 63635
grep -Fxq "$FILENAME" my_list.txt

Le statut de sortie est 0 (vrai) si le nom a été trouvé, 1 (faux) si non, donc :

if grep -Fxq "$FILENAME" my_list.txt
then
    # code if found
else
    # code if not found
fi

Explication

Voici les sections pertinentes de la page de manuel de grep :

grep [options] PATTERN [FILE...]

-F , --fixed-strings

        Interprétez PATTERN comme une liste de chaînes de caractères fixes, séparées par des retours à la ligne, dont l'une d'entre elles doit être comparée.

-x , --line-regexp

        Sélectionnez uniquement les correspondances qui correspondent exactement à la ligne entière.

-q , --quiet , --silent

        Quiet ; n'écrit rien sur la sortie standard. Sortir immédiatement avec le statut zéro si une correspondance est trouvée, même si une erreur a été détectée. Voir également l'option -s ou --no-messages option.

Traitement des erreurs

Comme cela a été souligné à juste titre dans les commentaires, l'approche ci-dessus traite silencieusement les cas d'erreur comme si la chaîne était trouvée. Si vous souhaitez traiter les erreurs d'une manière différente, vous devrez omettre l'élément -q et détecter les erreurs en fonction de l'état de sortie :

Normalement, l'état de sortie est 0 si les lignes sélectionnées sont trouvées et 1 sinon. Mais l'état de sortie est 2 si une erreur s'est produite, à moins que la commande -q ou --quiet ou --silent est utilisée et une ligne sélectionnée est trouvée. Notez cependant que POSIX impose seulement, pour des programmes tels que grep , cmp et diff Il est donc conseillé, pour des raisons de portabilité, d'utiliser une logique qui teste cette condition générale au lieu d'une stricte égalité avec 2.

Pour supprimer la sortie normale de grep vous pouvez le rediriger vers /dev/null . Notez que l'erreur standard reste non dirigée, donc tout message d'erreur qui grep pourrait s'afficher sur la console comme vous le souhaitez.

Pour gérer les trois cas, nous pouvons utiliser une case déclaration :

case `grep -Fx "$FILENAME" "$LIST" >/dev/null; echo $?` in
  0)
    # code if found
    ;;
  1)
    # code if not found
    ;;
  *)
    # code if an error occurred
    ;;
esac

2 votes

Si j'exécute cette commande depuis bash script comment attraper 0 ou 1 dans une variable ?

7 votes

@Toren Le statut de sortie le plus récent peut être consulté en utilisant $? . vous pouvez également utiliser la commande grep en plus de la commande if (comme indiqué dans la réponse actualisée).

5 votes

Vous pouvez utiliser grep -Fqx "$FILENAME" et vous n'avez pas à vous soucier des caractères regex dans le contenu des variables et vous n'aurez pas à les utiliser dans la chaîne de recherche.

104voto

Kuf Points 5775

Concernant la solution suivante :

grep -Fxq "$FILENAME" my_list.txt

Au cas où vous vous demanderiez (comme moi) ce qu'est la -Fxq signifie en clair :

  • F : Affecte la façon dont PATTERN est interprété (chaîne fixe au lieu d'une regex)
  • x : Correspondre à la ligne entière
  • q : Shhhhh... impression minimale

À partir du fichier de manuel :

-F, --fixed-strings
    Interpret  PATTERN  as  a  list of fixed strings, separated by newlines, any of which is to be matched.
    (-F is specified by POSIX.)
-x, --line-regexp
    Select only those matches that exactly match the whole line.  (-x is specified by POSIX.)
-q, --quiet, --silent
    Quiet; do not write anything to standard output.  Exit immediately with zero status  if  any  match  is
          found,  even  if  an error was detected.  Also see the -s or --no-messages option.  (-q is specified by
          POSIX.)

5 votes

-F n'affecte pas le traitement du fichier, il affecte la façon dont PATTERN est interprété. Typiquement, PATTERN est interprété comme une regex, mais avec -F il sera interprété comme une chaîne fixe.

44voto

Luca Borrione Points 3038

Trois méthodes dans mon esprit :

1) Test court pour un nom dans un chemin (je ne suis pas sûr que ce soit votre cas)

ls -a "path" | grep "name"

2) Test court pour une chaîne de caractères dans un fichier

grep -R "string" "filepath"

3) Plus long bash script utilisant regex :

#!/bin/bash

declare file="content.txt"
declare regex="\s+string\s+"

declare file_content=$( cat "${file}" )
if [[ " $file_content " =~ $regex ]] # please note the space before and after the file content
    then
        echo "found"
    else
        echo "not found"
fi

exit

Cela devrait être plus rapide si vous avez pour tester plusieurs chaînes sur un fichier le contenu en utilisant une boucle, par exemple en changeant la regex à tout moment.

11 votes

Pourquoi les espaces sont-ils nécessaires avant et après le $file_contenet ?

0 votes

Plus 1 pour la solution rapide et une solution plus généralisable

0 votes

Quel est le =~ et où puis-je en apprendre davantage à ce sujet ?

23voto

imwilsonxu Points 1256

C'est plus simple :

if grep "$filename" my_list.txt > /dev/null
then
   ... found
else
   ... not found
fi

Conseil : envoyer à /dev/null si vous voulez le statut de sortie de la commande, mais pas les sorties.

1 votes

Ou utiliser -q qui est identique à --quiet :)

0 votes

S'accordent sur le -q également la meilleure réponse ici, et est en quatrième position. aucune justice dans ce monde.

6voto

lecodesportif Points 1892

Si j'ai bien compris votre question, ceci devrait faire ce dont vous avez besoin.

  1. vous pouvez spécifier le répertoire que vous souhaitez ajouter à travers la variable $check
  2. si le répertoire est déjà dans la liste, la sortie est "dir already listed".
  3. si le répertoire n'est pas encore dans la liste, il est ajouté à ma_liste.txt

En une ligne : check="/tmp/newdirectory"; [[ -n $(grep "^$check\$" my_list.txt) ]] && echo "dir already listed" || echo "$check" >> my_list.txt

0 votes

Vous n'avez pas besoin de tester la sortie de grep, vous pouvez simplement utiliser grep -q et appeler grep directement depuis if comme le fait Thomas dans sa réponse. De plus, la question n'incluait pas de vérifier si le répertoire existe avant de l'ajouter à la liste (il pourrait s'agir d'une liste de répertoires supprimés, après tout).

0 votes

J'ai supprimé l'exemple script, il n'apportait rien à la réponse donnée par Thomas.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X