Welchen Nutzen hat der Befehl: in der Shell-Skripterstellung, da er explizit nichts bewirkt?

27

In der Antwort auf diese Frage zu Kommentaren in Shell-Skripten wird angegeben, dass der :Befehl null ist, der explizit nichts tut (aber nicht für Kommentare verwendet werden soll).

Was wäre der Nutzen eines Befehls, der absolut nichts tut?

DQdlM
quelle
2
Siehe auch diese Frage, die hier eine noch bessere Antwort hat , nämlich dass :es sich um eine eingebaute Frage handeln muss , während dies truenicht der Fall ist, die den Gültigkeitsbereich von Variablen beeinflusst
Old Pro
2
Es tut ausdrücklich nichts anmutig .
mikeserv

Antworten:

19

Ich benutze normalerweise truein Schleifen; Ich denke es ist klarer:

while true; do
    ...
done

Der einzige Ort, den ich gefunden habe, der :wirklich praktisch ist, sind case-Anweisungen, wenn Sie etwas abgleichen müssen, aber eigentlich nichts tun möchten. Beispielsweise:

case $answer in
    ([Yy]*) : ok ;;
    (*)     echo "stop."; exit 1 ;;
esac
Verrückter Wissenschaftler
quelle
3
Genau. truefür eine Bedingung, :für eine NOP.
JW013
1
Bash akzeptiert case a in a ) ;; esac. Gibt es einige Muscheln, die das nicht akzeptieren?
Kaz
@Kaz: Entsprechend der POSIX - Shell - Grammatik , case ${var} in value);; *) do_something;; esacist akzeptabel. Der :Befehl wird für leere Fälle nicht benötigt.
Richard Hansen
12

Ursprünglich wurde damit festgestellt, dass es sich im Gegensatz zu C-kompilierten Programmen um ein Bourne-Shell-Programm handelte. Dies war vor Shebang und mehreren Skriptsprachen (csh, perl). Sie können weiterhin ein Skript ausführen, das mit den folgenden Schritten beginnt ::

$ echo : > /tmp/xyzzy
$ chmod +x /tmp/xyzzy
$ ./xyzzy

In der Regel wird das Skript gegen $SHELL(oder /bin/sh) ausgeführt.

Seitdem besteht der Hauptzweck darin, die Argumente zu bewerten. Ich benutze noch:

: ${EDITOR:=vim}

um einen Standardwert in einem Skript festzulegen.

Arcege
quelle
11

: ist nützlich zum Schreiben von Schleifen, die von innen abgeschlossen werden müssen.

while :
do
    ...stuff...
done

Dies wird für immer ausgeführt, sofern nicht breakoder exitaufgerufen wird oder die Shell ein Beendigungssignal empfängt.

Kyle Jones
quelle
3
Ich denke, das while true; do ...; doneteilt dem Leser Absichten besser mit alswhile :; do ...; done
Richard Hansen
9

Wenn Sie in Shell-Skripten eine "wenn nicht" -Anweisung wünschen, verwenden Sie entweder eine "nicht" -Bedingung, die für einige der Tests doof aussehen kann, oder Sie verwenden ':' in der true-Klausel, mit echtem Code in der false- Klausel.

if [ some-exotic-condition ]
then
    :
else
    # Real code here
fi

Der "exotische Zustand" könnte etwas sein, das Sie nicht negieren möchten, oder das ist nur viel klarer, wenn Sie keine "negative Logik" verwenden.

Bruce Ediger
quelle
1
Es wird auch in Skripten autoconfangezeigt, die von generiert wurden, da es viel einfacher ist, einen Standard :für leere Zweige hinzuzufügen, als herauszufinden, wie die Bedingung umgekehrt werden kann.
Dietrich Epp
2
Ich kann nicht sehen, wie doof es ist , in einem !davor zu [ some-exotic-condition ]stecken, aber ein überflüssiges, : elsenachdem es nicht doof ist.
Kaz
@ Kaz hat einen guten Punkt. Denken wir daran, dass es bestenfalls schwierig ist, exotische Bedingungen zu finden. Wenn Sie alles negieren müssen, ist das eine Sache, aber es könnte die Bedingung nur weniger klar machen. Tut ein '!' den ganzen Zustand annullieren oder nur den ersten Term? Manchmal ist es am besten, eine ':' - Klausel zu haben.
Bruce Ediger
Das !Token negiert ein gesamtes Befehlspipe-Element. while ! grep ... ; do ... done oder if ! [ ... ] ; then ... fi. Es ist im Grunde genommen außerhalb der test/[]Syntax. Siehe: pubs.opengroup.org/onlinepubs/9699919799/utilities/…
Kaz
5

Ich habe dies immer nur zusätzlich zum Zeichen # zum vorübergehenden Auskommentieren einer Zeile verwendet, in einer Situation, in der das Auskommentieren der Zeile aufgrund eines Fehlers in der Shell-Grammatik, der darin besteht, keine leere Befehlsfolge zuzulassen, zu einem Syntaxfehler führt :

if condition ; then
    :# temporarily commented out command
fi

Ohne das: haben wir eine fehlende Befehlsfolge, was ein Syntaxfehler ist.

Kaz
quelle
4

Es gibt zwei Fälle, in denen ich :nützlich finde :

Standardmäßige Variablenzuweisungen

#!/bin/sh

# set VAR to "default value" if not already set in the environment
: "${VAR=default value}"

# print the value of the VAR variable.  Note that POSIX says the behavior
# of echo is implementation defined if the first argument is '-n' or if any
# argument contains a '\', so use printf instead of echo.
printf '%s\n' "VAR=${VAR}"

Auf diese bequeme Weise können Benutzer Ihres Shell-Skripts eine Einstellung überschreiben, ohne das Skript zu bearbeiten. ( Befehlszeilenargumente sind jedoch besser, da Sie nicht das Risiko eines unerwarteten Verhaltens eingehen, wenn der Benutzer zufällig die Variable hat, die Sie in seiner exportierten Umgebung verwenden.) So würde der Benutzer die Einstellung überschreiben:

VAR="other value" ./script

In der ${VAR=value}Syntax wird festgelegt, dass auf gesetzt werden soll VAR, valuewenn dies VARnoch nicht geschehen ist. Erweitern Sie dann auf den Wert der Variablen. Da uns der Wert der Variablen noch nicht wichtig ist, wird er als Argument an den Befehl no-op übergeben :, um ihn zu verwerfen.

Obwohl :es sich um einen No-Op-Befehl handelt, wird die Erweiterung von der Shell ausgeführt (nicht vom :Befehl!), Bevor der :Befehl ausgeführt wird, sodass die Variablenzuweisung weiterhin erfolgt (falls zutreffend).

Es wäre auch akzeptabel, truestattdessen einen oder einen anderen Befehl zu verwenden :, aber der Code wird schwieriger zu lesen, da die Absicht weniger klar ist.

Das folgende Skript würde auch funktionieren:

#!/bin/sh

# print the value of the VAR variable.  Note that POSIX says the behavior
# of echo is implementation defined if the first argument is '-n' or if any
# argument contains a '\', so use printf instead of echo.
printf '%s\n' "VAR=${VAR=default value}"

Aber das Obige ist viel schwieriger zu pflegen. Wenn eine Zeile ${VAR}über dieser printfZeile hinzugefügt wird, muss die Standardzuweisungserweiterung verschoben werden. Wenn der Entwickler vergisst, diese Zuweisung zu verschieben, wird ein Fehler gemeldet.

Etwas, das in einen leeren bedingten Block eingefügt werden kann

Leere bedingte Blöcke sollten im Allgemeinen vermieden werden, aber manchmal sind sie nützlich:

if some_condition; then
    # todo:  implement this block of code; for now do nothing.
    # the colon below is a no-op to prevent syntax errors
    :
fi

Einige Leute argumentieren, dass ein leerer True- ifBlock das Lesen von Code erleichtern kann, anstatt den Test zu negieren. Beispielsweise:

if [ -f foo ] && bar || baz; then
    :
else
    do_something_here
fi

ist wohl leichter zu lesen als:

if ! [ -f foo ] || ! bar && ! baz; then
    do_something_here
fi

Ich glaube jedoch, dass es einige alternative Ansätze gibt, die besser sind als ein leerer wahrer Block:

  1. Setzen Sie die Bedingung in eine Funktion:

    exotic_condition() { [ -f foo ] && bar || baz; }
    
    if ! exotic_condition; then
        do_something_here
    fi
    
  2. Setzen Sie die Bedingung in geschweifte Klammern (oder Klammern, aber Klammern rufen einen Subshell-Prozess hervor und alle Änderungen an der Umgebung innerhalb der Subshell sind außerhalb der Subshell nicht sichtbar), bevor Sie negieren:

    if ! { [ -f foo ] && bar || baz; } then
        do_something_here
    fi
    
  3. Verwenden Sie ||anstelle von if:

    [ -f foo ] && bar || baz || {
        do_something_here
    }
    

    Ich bevorzuge diesen Ansatz, wenn es sich bei der Reaktion um eine einfache Einzeilerreaktion handelt, z.

    log() { printf '%s\n' "$*"; }
    error() { log "ERROR: $*" >&2; }
    fatal() { error "$@"; exit 1; }
    
    [ -f foo ] && bar || baz || fatal "condition not met"
    
Richard Hansen
quelle
1

In der alten Pre-Bourne-Shell in früheren UNIX-Versionen war der :Befehl ursprünglich zum Angeben von Bezeichnungen für vorgesehen goto(es war ein separater Befehl, der die Eingabe an die Stelle weiterleitet, an der sich die Bezeichnung befindet) Shell weiß über. ifwar auch ein separater Befehl.) Es wurde bald für Kommentare verwendet, bevor es eine Kommentarsyntax gab ( #wurde für die Rücktaste verwendet) und heutzutage gibt es so gut wie Kompatibilität.

Random832
quelle
0

Sie können es nicht nur als Anweisung verwenden, die nichts bewirkt, sondern Sie können auch einzelne Anweisungen auskommentieren, indem Sie sie in Argumente für Folgendes umwandeln:.

Martin Stein
quelle
Es wird kein genauer Kommentar sein, da : echo write this line > myfileimmer noch eine leere Datei erstellt wird.
Arcege
5
Wie im Link in der Frage erläutert, :ist KEIN Kommentar-Mechanismus ausreichend.
JW013