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

337

J'ai un fichier qui contient des noms de répertoire:

my_list.txt :

/tmp
/var/tmp

Je voudrais archiver Bash avant d'ajouter un nom de répertoire si ce nom existe déjà dans le fichier.

Toren
la source
Pour trouver toutes les chaînes à l'intérieur d'un fichier, vous pouvez exécuter grep dans la boucle FOR: unix.stackexchange.com/a/462445/43233
Noam Manos

Réponses:

655
grep -Fxq "$FILENAME" my_list.txt

Le statut de sortie est 0 (vrai) si le nom a été trouvé, 1 (faux) sinon, 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 pourgrep :

grep [options] PATTERN [FILE...]

-F, --fixed-strings

        Interprétez PATTERN comme une liste de chaînes fixes, séparées par des sauts de ligne, dont chacune doit être mise en correspondance.

-x, --line-regexp

        Sélectionnez uniquement les correspondances qui correspondent exactement à toute la ligne.

-q, --quiet,--silent

        Silencieux; n'écrivez rien sur la sortie standard. Quittez immédiatement avec l'état zéro si une correspondance est trouvée, même si une erreur a été détectée. Voir également l' option -sou --no-messages.

La gestion des erreurs

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

Normalement, l'état de sortie est 0 si des lignes sélectionnées sont trouvées et 1 sinon. Mais l'état de sortie est 2 si une erreur s'est produite, sauf si l' option -qou --quietou --silentest utilisée et qu'une ligne sélectionnée est trouvée. Notez toutefois que seuls les mandats, POSIX pour des programmes tels que grep, cmpet diffque l'état de sortie en cas d'erreur soit supérieur à 1; 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 la rediriger vers /dev/null. Notez que l'erreur standard reste non dirigée, donc tous les messages d'erreur qui greppourraient s'imprimer se retrouveront sur la console comme vous le voudriez probablement.

Pour gérer les trois cas, nous pouvons utiliser une caseinstruction:

case `grep -Fx "$FILENAME" "$LIST" >/dev/null; echo $?` in
  0)
    # code if found
    ;;
  1)
    # code if not found
    ;;
  *)
    # code if an error occurred
    ;;
esac
Thomas
la source
2
Si j'exécute cette commande à partir du script bash, comment attraper 0 ou 1 dans une variable?
Toren
6
@Toren Le statut de sortie le plus récent est accessible à l'aide de $?. vous pouvez également utiliser la commande grep à côté de l' ifinstruction (comme indiqué dans la réponse mise à jour).
Shawn Chin
5
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.
pause jusqu'à nouvel ordre.
4
Quelques notes pour les gens qui regardent cette réponse: 1) En bash, 0 est toujours vrai et tout le reste est toujours faux 2) N'utilisez l'indicateur -x que si vous voulez que la ligne entière corresponde exactement. Si vous voulez simplement savoir si votre chaîne existe dans le fichier, laissez-la désactivée. Si vous voulez trouver si votre chaîne existe exactement mais sans correspondre nécessairement à une ligne entière (c'est-à-dire comme un mot entier), utilisez -w.
Schmick
1
Je ne comprends pas le -q / --silentbesoin? Son dit faussement "tout va bien" à bash même si une erreur se produit. Si j'ai bien compris. Semble un concept défectueux pour ce cas.
redanimalwar
90

Concernant la solution suivante:

grep -Fxq "$FILENAME" my_list.txt

Au cas où vous vous demandez (comme je l'ai fait) ce que -Fxqsignifie en anglais simple:

  • F: Affecte la façon dont le motif est interprété (chaîne fixe au lieu d'une expression régulière)
  • x: Correspondre à toute la ligne
  • q: Shhhhh ... impression minimale

Depuis le fichier man:

-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.)
Kuf
la source
5
-F n'affecte pas le traitement des fichiers, il affecte la façon dont le MOTIF est interprété. En règle générale, PATTERN est interprété comme une expression régulière, mais avec -F, il sera interprété comme une chaîne fixe.
Adam S
41

Trois méthodes dans mon esprit:

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

ls -a "path" | grep "name"


2) Court test pour une chaîne dans un fichier

grep -R "string" "filepath"


3) Script bash plus long utilisant l'expression régulière:

#!/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 devez tester plusieurs chaînes sur un contenu de fichier en utilisant une boucle, par exemple en changeant l'expression régulière à n'importe quel cicle.

Luca Borrione
la source
10
Pourquoi les espaces sont-ils nécessaires avant et après le $ file_contenet?
EminezArtus
Plus 1 pour la solution rapide et une solution plus généralisable
Wassadamo
19

Manière plus simple:

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

Astuce: envoyez à /dev/nullsi vous voulez l'état de sortie de la commande, mais pas les sorties.

imwilsonxu
la source
1
ou utilisez la -qmême chose que --quiet:)
rogerdpack
d'accord sur la -qmeilleure réponse également ici, et est la quatrième place. pas de justice dans ce monde.
tatsu
15

Le moyen le plus simple et le plus simple serait:

isInFile=$(cat file.txt | grep -c "string")


if [ $isInFile -eq 0 ]; then
   #string not contained in file
else
   #string is in file at least once
fi

grep -c renverra le nombre de fois où la chaîne se produit dans le fichier.

Christian737
la source
5

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

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

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

lecodesportif
la source
Vous n'avez pas besoin de tester la sortie de grep, vous pouvez simplement utiliser grep -qet appeler grep directement ifcomme Thomas le fait dans sa réponse. De plus, la question ne comprenait 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).
sorpigal
J'ai supprimé l'exemple de script, cela n'ajoutait rien à la réponse donnée par Thomas.
lecodesportif
3

Si vous souhaitez simplement vérifier l'existence d'une ligne, vous n'avez pas besoin de créer de fichier. Par exemple,

if grep -xq "LINE_TO_BE_MATCHED" FILE_TO_LOOK_IN ; then
  # code for if it exists
else
  # code for if it does not exist
fi  
gordon
la source
3

Ma version utilisant fgrep

  FOUND=`fgrep -c "FOUND" $VALIDATION_FILE`
  if [ $FOUND -eq 0 ]; then
    echo "Not able to find"
  else
    echo "able to find"     
  fi  
Rudy
la source
Je ne vois pas d' -coption dansfgrep --help
Nam G VU
3
grep -E "(string)" /path/to/file || echo "no match found"

L'option -E oblige grep à utiliser des expressions régulières

David Okwii
la source
1

La solution de @ Thomas n'a pas fonctionné pour moi pour une raison quelconque, mais j'avais une chaîne plus longue avec des caractères spéciaux et des espaces, donc j'ai juste changé les paramètres comme ceci:

if grep -Fxq 'string you want to find' "/path/to/file"; then
    echo "Found"
else
    echo "Not found"
fi

J'espère que cela aide quelqu'un

Incarné29
la source
0

Une solution sans grep, fonctionne pour moi:

MY_LIST=$( cat /path/to/my_list.txt )



if [[ "${MY_LIST}" == *"${NEW_DIRECTORY_NAME}"* ]]; then
  echo "It's there!"
else
echo "its not there"
fi

basé sur: https://stackoverflow.com/a/229606/3306354

AndrewD
la source
1
Utilise trop de mémoire si le fichier est volumineux. grep -qdécrit dans la réponse acceptée est l'approche la plus efficace.
codeforester
0
grep -Fxq "String to be found" | ls -a
  • grep will vous aide à vérifier le contenu
  • ls listera tous les fichiers
Shinoy Shaji
la source
@ThomWiggers J'ai essayé la même chose et cela a fonctionné pour moi.
Shinoy Shaji
-1
if grep -q "$Filename$" my_list.txt
   then
     echo "exist"
else 
     echo "not exist"
fi
Triangle
la source