Wie füge ich eine Zeichenfolgensuche mit dem Befehl grep in die if-Anweisung ein?

11

Ich möchte mehrere Zeichenfolgen in zwei Dateien suchen. Wenn in beiden Dateien eine Zeichenfolge gefunden wird, machen Sie etwas. Wenn eine Zeichenfolge nur in einer Datei gefunden wird, machen Sie eine andere Sache.

Meine Befehle sind die nächsten:

####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

Ist es richtig, die Aussagen zu leugnen und zu bestätigen? pd Ich benutze die KSH-Shell.

Danke im Voraus.

Mareyes
quelle

Antworten:

11

Versuche dies:

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 kann in mehreren Dateien nach Mustern suchen, sodass kein OR / NOT-Operator verwendet werden muss.
msp9011
quelle
Gibt es in jedem Befehl einen minimalen Unterschied zwischen "-wq" und "-qw"?
Mareyes
2
Nein, beide bieten die Optionen -q und -w für grep.
Glenn Jackman
3
Warum keine Anführungszeichen zur Variablenerweiterung?
D. Ben Knoble
@ D.BenKnoble ist es richtig: "$ user" "$ file1"?
Mareyes
1
@ Stareyes ja das ist richtig
D. Ben Knoble
13

Andere 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
quelle
Hey, danke für den optionalen Weg. Es funktioniert genug gut !! funktioniert perfekt in einem anderen Skript, das ich habe.
Mareyes
Das ist eine großartige Idee! Immer lernen.
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 

Hinweis: ((n++))ist eine ksh-Erweiterung (wird auch von zshund unterstützt bash). In der POSIX- shSyntax benötigen Sie n=$((n + 1))stattdessen.

Luciano Andress Martini
quelle
Oh, tut mir leid, ich habe $ ((n ++)) anstelle von ((n ++)) eingegeben. Vergessen.
Luciano Andress Martini
1
Beachten Sie, dass n=$((n++))niemals den Wert von n wird sich ändern , da n++ist Post Schritt: den gibt aktuelle Wert von n, dann Schritten n; Dann setzen Sie die Variable auf den zurückgegebenen Wert, der das ursprüngliche n war.
Glenn Jackman
Wenn Sie davon ausgehen können, dass Ihre Dateinamen keine Zeilenumbrüche enthalten local IFS=$'\n'; matches=( $(grep -lw "$users" "$file1" "$file2") ), und verwenden Sie case "${#matches[@]" in. @glenn: Diese Idee gilt auch für Ihre Antwort, um mehrfache Aufrufe von zu vermeiden grep. Piping es grep -l | wc -lwürde auch funktionieren.
Peter Cordes
@ Peter, das wäre es wert, als separate Antwort IMO geschrieben zu werden.
Stephen Kitt
@StephenKitt: Ich wollte nicht, weil ich keinen kugelsicheren Weg finden konnte, um mit beliebigen Dateinamenzeichen umzugehen. Aber da du denkst, dass es sich lohnt, habe ich es trotzdem geschrieben.
Peter Cordes
2

Wenn Ihre Dateinamen keine Zeilenumbrüche enthalten, können Sie mehrere Aufrufe von vermeiden, grepindem Sie die Namen der übereinstimmenden Dateien von grep drucken lassen und die Ergebnisse zählen.

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

Die Anzahl der Übereinstimmungen beträgt "${#matches[@]}".

Möglicherweise gibt es hier eine Möglichkeit grep --null -lw, aber ich bin mir nicht sicher, wie ich die Ausgabe analysieren soll . Bash var=( array elements )hat keine Möglichkeit, \0stattdessen ein Trennzeichen zu verwenden \n. Vielleicht kann Bash's mapfileBuiltin das? Aber wahrscheinlich nicht, weil Sie das Trennzeichen mit angeben -d string.


Sie könnten count=$(grep -l | wc -l), aber dann haben Sie zwei externe Prozesse, so dass Sie grepdie beiden Dateien genauso gut getrennt ausführen können. (Der Unterschied zwischen Startaufwand grepund wcStartaufwand ist im Vergleich zu Fork + Exec + Dynamic Linker gering, um überhaupt einen separaten Prozess zu starten.)

Außerdem finden wc -lSie mit nicht heraus, welche Datei übereinstimmt.


Wenn die Ergebnisse in einem Array erfasst wurden, ist dies möglicherweise bereits das, was Sie möchten, oder wenn genau 1 Übereinstimmung vorliegt, können Sie überprüfen, ob es sich um die erste Eingabe handelt oder nicht.

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

$matchesist eine Abkürzung für ${matches[0]}das erste Array-Element.

Peter Cordes
quelle
@ StephenKitt: Ich meinte -z, nicht -0, oops. Ja, das würde grep dazu bringen, die Dateinamen getrennt zu drucken, \0wie ich bereits in der Antwort erwähnt habe, aber wie können Sie bash dazu bringen, das zu analysieren? Sie können die IFS=$'\0'Stilausgabe nicht find -print0direkt mit bash festlegen oder im Grunde nichts anderes tun .
Peter Cordes
Ja, ich habe zu schnell gelesen, diesen Absatz verpasst und die Dinge nicht durchdacht (weshalb ich meinen Kommentar gelöscht habe). Es mag durchaus einen Weg geben, aber ich kann mir gerade keinen vorstellen, und wie Sie sagen, lohnt es sich nicht, die Lösung zu komplex zu machen, wenn es grepsowieso nur ein paar gibt, mit denen man sich befassen muss.
Stephen Kitt
1
mapfile -d $'\0'Art von Arbeiten, aber es ersetzt die Zeilenumbrüche durch Leerzeichen (ich habe nicht versucht, zu optimieren IFS).
Stephen Kitt