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
Das funktioniert auch wie erwartet. Mir fällt jedoch auf, dass ich nicht weiß, was die Klammern dort tatsächlich tun. Ich möchte sie nur zum Gruppieren verwenden, aber gibt es tatsächlich eine Unterschale? (Woran erkenne ich das?) Wenn ja, gibt es eine Möglichkeit, die Bedingungen zu gruppieren, ohne eine Unterschale zu erzeugen?
bash
shell-script
Platzhalter
quelle
quelle
if ! [ -f file1 ] || ! [ -f file 2 ] || ! [ -f file3 ] ; then
Aber ich hätte gerne eine allgemeinere Antwort.if [[ ! -f file1 ]] && [[ ! -f file2 ]]; then
if [ ! 1 -eq 2 ] && [ ! 2 -eq 3 ]; then echo yep; fi
und es funktioniert. Ich schreibe nur aus Gewohnheit immer Tests mit doppelten Klammern. Außerdem, um sicherzustellen, dass es nicht istbash
, habe ich weiter getestetif /bin/test ! 1 -eq 2 && /bin/test ! 2 -eq 3 ; then echo yep; fi
und es funktioniert auch so.[ ! -e file/. ] && [ -r file ]
Verzeichnisse. negiere es wie du willst. Natürlich ist es das, was-d
tut.Antworten:
Sie müssen
{ list;}
anstelle von verwenden(list)
:Beide sind Gruppierungsbefehle , aber
{ list;}
Befehle in der aktuellen Shell-Umgebung aus.Beachten Sie, dass das
;
in{ list;}
benötigt wird, um die Liste vom}
umgekehrten Wort abzugrenzen. Sie können auch ein anderes Trennzeichen verwenden. Das Leerzeichen (oder ein anderes Trennzeichen) nach{
ist ebenfalls erforderlich.quelle
awk
öfter geschweifte Klammern verwende als inbash
), ist die Notwendigkeit eines Leerzeichens nach der offenen geschweiften Klammer. Sie erwähnen "Sie können auch andere Trennzeichen verwenden"; irgendwelche Beispiele?zsh
können Sie Whitespace verwenden&
ist auch ein separator, den du benutzen kannst{ echo 1;:&}
, mehrere newline können auch benutzt werden.they must be separated from list by whitespace or another shell metacharacter.
In bash verwendenmetacharacter: | & ; ( ) < > space tab
. Für diese spezielle Aufgabe glaube ich, dass jeder von& ; ) space tab
ihnen funktionieren wird. .... .... Von zsh (als Kommentar)each sublist is terminated by
&',
&!', or a newline.
Sie können die
test
Funktionalität vollständig nutzen , um das zu erreichen, was Sie wollen. Aus der Manpage vontest
:Ihr Zustand könnte also so aussehen:
Verwenden Sie zum Negieren ausgeblendete Klammern:
quelle
Um eine komplexe Bedingung in der Shell portabel zu negieren, müssen Sie entweder De Morgans Gesetz anwenden und die Negation in den
[
Aufrufen ganz nach unten schieben ...... oder du musst
then :; else
...if ! command
ist nicht tragbar und ist es auch nicht[[
.Wenn Sie keine vollständige Portabilität benötigen, schreiben Sie kein Shell-Skript . Sie sind tatsächlich mehr wahrscheinlich zu finden
/usr/bin/perl
auf einem zufällig ausgewählten Unix , als Sie sindbash
.quelle
!
ist POSIX, das heutzutage tragbar genug ist. Sogar Systeme, die noch mit der Bourne-Shell ausgeliefert werden, haben irgendwo anders im Dateisystem einen POSIX-Sh, mit dem Sie Ihre Standardsyntax interpretieren können./bin/sh
. Autoconf-Skripte machen das, was Sie vorschlagen, aber ich denke, wir alle wissen, wie wenig eine Bestätigung das ist.Andere haben die Gruppierung der
{
zusammengesetzten Befehle zur Kenntnis genommen.;}
Wenn Sie jedoch identische Tests an einem Satz durchführen , möchten Sie möglicherweise eine andere Art verwenden:... wie an anderer Stelle gezeigt wird mit
{ :;}
, gibt es keine Schwierigkeiten beim Verschachteln von zusammengesetzten Befehlen ...Beachten Sie, dass die oben genannten Tests (normalerweise) für reguläre Dateien. Wenn Sie nur nach vorhandenen , lesbaren Dateien suchen , die keine Verzeichnisse sind:
Wenn es Ihnen egal ist, ob es sich um Verzeichnisse handelt oder nicht:
... funktioniert für jede lesbare , zugängliche Datei, hängt aber wahrscheinlich für Fifos ohne Autoren.
quelle
Dies ist keine vollständige Antwort auf Ihre Hauptfrage, aber ich habe festgestellt, dass Sie in einem Kommentar Verbindungstests (lesbare Datei) erwähnen. z.B,
Sie können dies ein wenig festigen, indem Sie eine Shell-Funktion definieren. z.B,
Fügen Sie
[ $# = 1 ]
nach Belieben eine Fehlerbehandlung hinzu (z. B. ). Die ersteif
obige Aussage kann nun auf kondensiert werdenund Sie können dies noch weiter verkürzen, indem Sie den Namen der Funktion verkürzen. Ebenso können Sie die Negation definieren
not_readable_file()
(odernrf
kurz) und in die Funktion aufnehmen.quelle
readable_files() { [ $# -eq 0 ] && return 1 ; for file in "$@" ; do [ -f "$file" ] && [ -r "$file" ] || return 1 ; done ; }
(Haftungsausschluss: Ich habe diesen Code getestet und er funktioniert, aber es war kein Einzeiler. Ich habe Semikolons zum Posten hier hinzugefügt, aber ihre Platzierung nicht getestet.)if readable_files file1 file2 file3
würde nicht wissen, ob die Funktion AND oder OR ausführt - obwohl (a) ich denke, es ist ziemlich intuitiv, (b) wenn Sie das Skript nicht verteilen, ist es gut genug, wenn Sie es verstehen, und (c) da ich vorgeschlagen habe, den Funktionsnamen in Unklarheit abzukürzen, bin ich nicht in der Lage, über Lesbarkeit zu sprechen. … (Fortsetzung)for file in "$@" ; do
mitfor file do
- es standardmäßigin "$@"
, und (etwas Gegen intuitiv) das;
ist nicht nur unnötig , sondern eigentlich abgeraten.Sie können auch innerhalb der Klammertests negieren, um Ihren ursprünglichen Code wiederzuverwenden:
quelle
||
statt&&
||
würde das Prädikat ausgeführt, wenn eine der Dateien nicht vorhanden ist, nicht wenn und nur wenn nicht alle Dateien vorhanden sind. NICHT (A UND B UND C) ist dasselbe wie (NICHT A) UND (NICHT B) UND (NICHT C); siehe die ursprüngliche Frage.||
stattdessen aufteilen&&
. Siehe meinen ersten Kommentar unter meiner Frage.not (a and b)
ist das gleiche wie(not a) or (not b)
. Siehe en.wikipedia.org/wiki/De_Morgan%27s_lawsJa, die Befehle in der
(...)
werden in einer Subshell ausgeführt.Um zu testen, ob Sie sich in einer Subshell befinden, können Sie die PID Ihrer aktuellen Shell mit der PID der interaktiven Shell vergleichen.
Beispielausgabe, die zeigt, dass Klammern eine Subshell und geschweifte Klammern nicht erzeugen:
quelle
()
Spawn Subshell{}
...echo $$ && ( echo $$ )
versucht, die PIDs zu vergleichen und sie waren die gleichen, aber kurz bevor ich den Kommentar abgab, der Ihnen mitteilte, dass Sie sich geirrt hatten, habe ich eine andere Sache versucht.echo $BASHPID && ( echo $BASHPID )
gibt verschiedene PID'secho $BASHPID && { echo $BASHPID; }
gib die gleiche PID an, wie du es vorgeschlagen hast. Vielen Dank für die Ausbildung$BASHPID
, das ist das andere, was mir gefehlt hat.