Comment mettre une recherche de chaîne avec la commande grep dans l'instruction if?

11

Je souhaite rechercher plusieurs chaînes dans deux fichiers. Si une chaîne est trouvée dans les deux fichiers, faites quelque chose. Si une chaîne se trouve dans un seul fichier, faites autre chose.

Mes commandes sont les suivantes:

####This is for the affirmative sentence in both files
if grep -qw "$users" "$file1" && grep -qw "$users" "$file2"; then

####This is for the affirmative sentence in only one file, and negative for the other one
if grep -qw "$users" "$file1" ! grep -qw "$users" "$file2"; then

est-ce une manière correcte de nier et d'affirmer les déclarations? pd J'utilise le shell KSH.

Merci d'avance.

Mareyes
la source

Réponses:

11

Essaye ça:

if grep -wq -- "$user" "$file1" && grep -wq -- "$user" "$file2" ; then
   echo "string avail in both files"
elif grep -wq -- "$user" "$file1" "$file2"; then
   echo "string avail in only one file"
fi
  • grep peut rechercher des modèles dans plusieurs fichiers, donc pas besoin d'utiliser un opérateur OR / NOT.
msp9011
la source
Existe-t-il une différence minimale entre le "-wq" et le "-qw" dans chaque commande?
Mareyes
2
Non, les deux fournissent les options -q et -w à grep.
glenn jackman
3
Pourquoi pas de devis sur l'expansion variable?
D. Ben Knoble
@ D.BenKnoble est-il correctement: "$ user" "$ file1"?
Mareyes
1
@Mareyes oui c'est vrai
D. Ben Knoble
13

Une autre option:

grep -qw -- "$users" "$file1"; in_file1=$?
grep -qw -- "$users" "$file2"; in_file2=$?

case "${in_file1},${in_file2}" in
    0,0) echo found in both files ;;
    0,*) echo only in file1 ;;
    *,0) echo only in file2 ;;
      *) echo in neither file ;;
esac
glenn jackman
la source
Hé merci pour la manière optionnelle. Ça marche assez bien !! fonctionne parfaitement dans un autre script que j'ai.
Mareyes
C'est une bonne idée! Toujours entrain d'apprendre.
Joe
7
n=0

#Or if you have more files to check, you can put your while here. 
grep -qw -- "$users" "$file1" && ((n++))
grep -qw -- "$users" "$file2" && ((n++))

case $n in 
   1) 
       echo "Only one file with the string"
    ;;
   2)
       echo "The two files are with the string"
   ;;
   0)
       echo "No one file with the string"
   ;;
   *)
       echo "Strange..."
   ;;
esac 

Remarque: ((n++))est une extension ksh (également prise en charge par zshet bash). Dans la shsyntaxe POSIX , vous en auriez besoin à la n=$((n + 1))place.

Luciano Andress Martini
la source
Oh désolé, je tapais $ ((n ++)) au lieu de ((n ++)). Oublier.
Luciano Andress Martini
1
Notez que cela n=$((n++))ne changera JAMAIS la valeur de n car il n++est post- incrémenté: il renvoie la valeur actuelle de n, puis incrémente n; puis vous définissez la variable sur la valeur renvoyée qui était le n d'origine.
glenn jackman
Si vous pouvez supposer que vos noms de fichiers ne contiennent pas de nouvelles lignes local IFS=$'\n'; matches=( $(grep -lw "$users" "$file1" "$file2") ), et utilisez case "${#matches[@]" in. @glenn: cette idée s'applique également à votre réponse, afin d'éviter de multiples invocations de grep. La tuyauterie grep -l | wc -lfonctionnerait également.
Peter Cordes
@Peter qui mériterait d'être écrit comme une réponse distincte OMI.
Stephen Kitt
@StephenKitt: Je n'allais pas le faire parce que je n'ai pas trouvé de moyen pare-balles de gérer des caractères de nom de fichier arbitraires. Mais comme vous pensez que ça vaut le coup, je l'ai quand même écrit.
Peter Cordes
2

Si vos noms de fichiers ne contiennent pas de sauts de ligne, vous pouvez éviter plusieurs invocations grepen demandant à grep d'imprimer les noms des fichiers correspondants et de compter les résultats.

 local IFS=$'\n'    # inside a function.  Otherwise use some other way to save/restore IFS
 matches=( $(grep -lw "$users" "$file1" "$file2") )

Le nombre de correspondances est "${#matches[@]}".

Il pourrait y avoir un moyen d'utiliser grep --null -lwici, mais je ne sais pas comment analyser la sortie . Bash var=( array elements )n'a aucun moyen d'utiliser un \0délimiteur à la place de \n. Peut-être que la fonction mapfileintégrée de bash peut le faire? Mais probablement pas, car vous spécifiez le délimiteur avec -d string.


Vous pouvez le faire count=$(grep -l | wc -l), mais vous disposez de deux processus externes, vous pouvez donc tout aussi bien exécuter greples deux fichiers séparément. (La différence entre les frais généraux grepet le wcdémarrage est faible par rapport à fork + exec + truc de l'éditeur de liens dynamique pour démarrer un processus distinct).

De plus, wc -lvous ne savez pas quel fichier correspond.


Avec les résultats capturés dans un tableau, cela pourrait déjà être ce que vous voulez, ou s'il y a exactement 1 correspondance, vous pouvez vérifier s'il s'agissait de la première entrée ou non.

local IFS=$'\n'    # inside a function.  Otherwise use some other way to save/restore IFS
matches=( $(grep -lw "$users" "$file1" "$file2") )

# print the matching filenames
[[ -n $matches ]] && printf  'match in %s\n'  "${matches[@]}"

# figure out which input position the name came from, if there's exactly 1.
if [[ "${#matches[@]" -eq 1 ]]; then
    if [[ $matches == "$file1" ]];then
        echo "match in file1"
    else
        echo "match in file2"
    fi
fi

$matchesest un raccourci pour ${matches[0]}, le premier élément du tableau.

Peter Cordes
la source
@StephenKitt: Je voulais dire -z, non -0, oups. Oui, cela amènerait grep à imprimer les noms de fichiers séparés par \0comme je l'ai déjà mentionné dans la réponse, mais comment pouvez-vous obtenir que bash analyse cela? Vous ne pouvez pas définir IFS=$'\0'ou faire autre chose avec la find -print0sortie de style directement avec bash.
Peter Cordes
Oui, j'ai lu trop vite, j'ai raté ce paragraphe et je n'ai pas réfléchi (c'est pourquoi j'ai supprimé mon commentaire). Il pourrait bien y avoir un moyen, mais je ne peux pas y penser pour l'instant, et comme vous le dites, cela ne vaut pas la peine de rendre la solution trop complexe quand il n'y a que quelques greppoints à traiter de toute façon.
Stephen Kitt
1
mapfile -d $'\0'sorte de travaux, mais il remplace les nouvelles lignes par des espaces (je n'ai pas essayé de peaufiner IFS).
Stephen Kitt