it-swarm.com.de

Bash wenn nicht mehrere Bedingungen ohne Subshell?

Ich möchte mehrere Bedingungen in einer Shell-if-Anweisung kombinieren und die Kombination negieren. Ich habe den folgenden Arbeitscode für eine einfache Kombination von Bedingungen:

if [ -f file1 ] && [ -f file2 ] && [ -f file3 ] ; then
  # do stuff with the files
fi

Das funktioniert gut. Wenn ich es negieren möchte, kann ich den folgenden Arbeitscode verwenden:

if ! ( [ -f file1 ] && [ -f file2 ] && [ -f file3 ] ) ; then
  echo "Error: You done goofed."
  exit 1
fi
# do stuff with the files

Dies funktioniert auch wie erwartet. Mir fällt jedoch ein, dass ich nicht weiß, was die Klammern dort tatsächlich tun. Ich möchte, um sie nur zum Gruppieren zu verwenden, aber erzeugt sie tatsächlich eine Unterschale? (Wie kann ich das beurteilen?) Wenn ja, gibt es eine Möglichkeit, die Bedingungen zu gruppieren, ohne eine Unterschale zu erzeugen?

26
Wildcard

Sie müssen { list;} Anstelle von (list) Verwenden:

if ! { [ -f file1 ] && [ -f file2 ] && [ -f file3 ]; }; then
  : do something
fi

Beide sind Gruppierungsbefehle , aber { list;} Führt Befehle in der aktuellen Shell-Umgebung aus.

Beachten Sie, dass das ; In { list;} Erforderlich ist, um die Liste von } Reverse Word abzugrenzen. Sie können auch ein anderes Trennzeichen verwenden. Das Leerzeichen (oder ein anderes Trennzeichen) nach { Ist ebenfalls erforderlich.

35
cuonglm

Sie können die Funktion test vollständig verwenden, um das zu erreichen, was Sie möchten. Aus der Manpage von test:

 ! expression  True if expression is false.
 expression1 -a expression2
               True if both expression1 and expression2 are true.
 expression1 -o expression2
               True if either expression1 or expression2 are true.
 (expression)  True if expression is true.

Ihr Zustand könnte also so aussehen:

if [ -f file1 -a -f file2 -a -f file3 ] ; then
    # do stuff with the files
fi

Verwenden Sie zum Negieren von Klammern:

if [ ! \( -f file1 -a -f file2 -a -f file3 \) ] ; then
    echo "Error: You done goofed."
    exit 1
fi
# do stuff with the files
8
woodengod

Um eine komplexe Bedingung in Shell portabel zu negieren, müssen Sie entweder De Morgans Gesetz anwenden und die Negation ganz nach innen drücken der [ ruft ...

if [ ! -f file1 ] || [ ! -f file2 ] || [ ! -f file3 ]
then
    # do stuff
fi

... oder Sie müssen then :; else verwenden ...

if [ -f file1 ] && [ -f file2 ] && [ -f file3 ]
then :
else
  # do stuff
fi

if ! command Ist nicht portabel verfügbar und [[ Auch nicht.

Wenn Sie keine vollständige Portabilität benötigen, schreiben Sie kein Shell-Skript. Sie werden tatsächlich mehr wahrscheinlich /usr/bin/Perl Unter einem zufällig ausgewählten Unix finden, als Sie bash sind.

7
zwol

andere haben das { zusammengesetzter Befehl ;} Gruppierung, aber wenn Sie identische Tests an einem set durchführen, möchten Sie möglicherweise eine andere Art verwenden:

if  ! for f in file1 file2 file3
      do  [ -f "$f" ] || ! break
      done
then  : do stuff
fi

... wie an anderer Stelle mit { :;}, es gibt keine Schwierigkeiten beim Verschachteln von zusammengesetzten Befehlen ...

Beachten Sie, dass die oben genannten (normalerweise) Tests für reguläre Dateien. Wenn Sie nur nach vorhandenen, lesbaren Dateien suchen, die keine Verzeichnisse sind:

if  ! for f in file1 file2 file3
      do  [ ! -d "$f" ] && 
          [   -r "$f" ] || ! break
      done
then  : do stuff
fi

Wenn es Ihnen egal ist, ob es sich um Verzeichnisse handelt oder nicht:

if  ! command <file1 <file2 <file3
then  : do stuff
fi

... funktioniert für jede lesbare, zugängliche Datei, hängt aber wahrscheinlich für Fifos ohne Autoren.

5
mikeserv

Dies ist keine vollständige Antwort auf Ihre Hauptfrage, aber ich habe festgestellt, dass Sie in einem Kommentar zusammengesetzte Tests (lesbare Datei) erwähnen. z.B.,

if [ -f file1 ] && [ -r file1 ] && [ -f file2 ] && [ -r file2 ] && [ -f file3 ] && [ -r file3 ]

Sie können dies ein wenig konsolidieren, indem Sie eine Shell-Funktion definieren. z.B.,

readable_file()
{
    [ -f "$1" ]  &&  [ -r "$1" ]
}

Fügen Sie die Fehlerbehandlung zu (z. B. [ $# = 1 ]) Nach Belieben hinzu. Die erste if -Anweisung oben kann jetzt auf komprimiert werden

if readable_file file1  &&  readable_file file2  &&  readable_file file3

und Sie können dies noch weiter verkürzen, indem Sie den Namen der Funktion kürzen. Ebenso können Sie not_readable_file() (oder kurz nrf) definieren und die Negation in die Funktion aufnehmen.

Sie können auch innerhalb der Klammertests negieren, um Ihren ursprünglichen Code wiederzuverwenden:

if [[ ! -f file1 ]] || [[ ! -f file2 ]] || [[ ! -f file3 ]] ; then
  # do stuff with the files
fi
0
DopeGhoti