Was ist der Anwendungsfall von noop [:] in bash?


123

Ich habe in bash (:) nach noop gesucht, konnte aber keine guten Informationen finden. Was ist der genaue Zweck oder Anwendungsfall dieses Operators?

Ich habe versucht zu folgen und es funktioniert so für mich:

[mandy@root]$ a=11
[mandy@root]$ b=20
[mandy@root]$ c=30
[mandy@root]$ echo $a; : echo $b ; echo $c
10
30

Bitte lassen Sie mich wissen, jeden Anwendungsfall dieses Betreibers in Echtzeit oder an jedem Ort, an dem die Verwendung obligatorisch ist.



Beachten Sie, dass das :integrierte System in Bourne Shell und Ksh sowie in Bash vorhanden ist.
Ghoti


6
Wie ist das keine echte Frage? Ich denke, das ist eine wirklich gute Frage. Ich habe sogar eine gute Verwendung dafür, aber ich kann keine Antwort posten.
Steven Lu

Antworten:


175

Es ist mehr aus historischen Gründen da. Der eingebaute Doppelpunkt entspricht :genau true. Es ist traditionell zu verwenden, truewenn der Rückgabewert wichtig ist, beispielsweise in einer Endlosschleife:

while true; do
  echo 'Going on forever'
done

Es ist traditionell zu verwenden, :wenn die Shell-Syntax einen Befehl erfordert, Sie aber nichts zu tun haben.

while keep_waiting; do
  : # busy-wait
done

Die :builtin Termine den ganzen Weg zurück in die Thompson Schale , war es vorhanden in Unix v6 . :war ein Label-Indikator für die gotoAussage der Thompson-Shell . Die Beschriftung kann ein beliebiger Text sein, der :gleichzeitig als Kommentarindikator fungiert (wenn dies nicht der Fall ist goto comment, : commenthandelt es sich tatsächlich um einen Kommentar). Die Bourne-Muschel hatte sie gotonur behalten :.

Eine gebräuchliche Redewendung, die verwendet :wird : ${var=VALUE}, ist , die festgelegt wird var, VALUEwenn sie nicht festgelegt wurde, und nichts tut, wenn sie varbereits festgelegt wurde. Dieses Konstrukt existiert nur in Form einer Variablensubstitution, und diese Variablensubstitution muss irgendwie Teil eines Befehls sein: Ein No-Op-Befehl ist gut geeignet.

Siehe auch Welchen Zweck erfüllt der eingebaute Doppelpunkt? .


11
Gute Zusammenfassung. Außerdem wurde die ursprüngliche Bourne-Shell nicht #für Kommentare (oder #!/bin/shShebangs) verwendet. Der :Befehl führte Kommentare ein und wehe dem naiven Programmierer, der in seinen Kommentaren eine schöne Schachtel mit Sternen gemacht hat: : ****(oder schlimmer noch : * * *).
Jonathan Leffler

3
@ JonathanLeffler: Schande über mich, aber ich verstehe es nicht. Was würde passieren : * * *?
DevSolar

7
Da :es sich um einen Befehl handelt, muss die Shell ihre Argumente noch verarbeiten, bevor sie feststellen kann, dass sie :ignoriert werden. Meistens lässt Sie die Shell nur zusätzliche Arbeit leisten, um sie *auf eine Liste von Dateien im aktuellen Verzeichnis zu erweitern. Es hat keinen Einfluss darauf, wie das Skript funktioniert.
Chepper

Ich nehme an, es könnte dazu führen, dass Ihr Skript fehlschlägt, wenn Sie in einem Verzeichnis mit vielen Dateien ausgeführt werden und die Glob-Erweiterung die Befehlslängenbeschränkung überschreitet. Vielleicht nur, wenn Sie auf haben set -e. Wie auch immer, hoffentlich können wir uns alle darauf einigen #:)
Jack O'Connor

2
Ich benutze manchmal, while : ; do ... ; donewenn ich eine schnelle und schmutzige Endlosschleife will. (Normalerweise gibt es eine sleepin der Schleife, und ich tippe Strg-C, um sie zu töten.)
Keith Thompson

21

Ich benutze es für if-Anweisungen, wenn ich den gesamten Code auskommentiere. Zum Beispiel haben Sie einen Test:

if [ "$foo" != "1" ]
then
    echo Success
fi

Sie möchten jedoch vorübergehend alles auskommentieren, was darin enthalten ist:

if [ "$foo" != "1" ]
then
    #echo Success
fi

Was dazu führt, dass bash einen Syntaxfehler ausgibt:

line 4: syntax error near unexpected token `fi'
line 4: `fi'

Bash kann keine leeren Blöcke (WTF) haben. Sie fügen also ein No-Op hinzu:

if [ "$foo" != "1" ]
then
    #echo Success
    :
fi

oder Sie können das No-Op verwenden, um die Zeilen zu kommentieren:

if [ "$foo" != "1" ]
then
    : echo Success
fi

12

Wenn Sie set- edann || :eine gute Möglichkeit ist , um nicht das Skript zu beenden , wenn ein Fehler passiert (es macht explizit übergeben).


1
Ich fand dies nützlich für bestimmte Anwendungen, die Shell-Skripte aufrufen. Zum Beispiel wird Automator auf dem Mac bei einem Fehler beendet und sagt Ihnen nicht warum. Aber der Einsatz || :dann später Umgang mit dem Fehler , sich mit einem exit 0finden Sie alle stdout und stderr in der App - Fenster lassen Anzeige während des Debuggens. Überlegen Sie, { foo ... 2>&1; } || :was viel einfacher ist als das Einrichten komplexerer Fallen und Wenn-Dann.
Beejor

7

Sie würden verwenden :, um einen Befehl anzugeben, der erfolgreich ist, aber nichts tut. In diesem Beispiel ist der Befehl "Ausführlichkeit" standardmäßig deaktiviert, indem er auf gesetzt wird :. Die Option 'v' aktiviert es.

#!/bin/sh
# example
verbosity=:                         
while getopts v OPT ; do          
   case $OPT in                  
       v)        
           verbosity=/bin/realpath 
       ;;
       *)
           exit "Cancelled"
       ;;             
   esac                          
done                              

# `$verbosity` always succeeds by default, but does nothing.                              
for i in * ; do                   
  echo $i $($verbosity $i)         
done                              

$ example
   file

$ example -v
   file /home/me/file  

Streng genommen bedeutet das Zuweisen eines Werts zu einer Variablen "etwas tun", weshalb in Ihrer case-Anweisung kein Fehler auftritt.
Qodeninja

@qodeninja: Niemand hat behauptet, dass die Variablenzuweisung "nichts getan hat"; Der Punkt ist, dass der $(verbosity $i)spätere (und bedingte) nichts tut.
Leichtigkeitsrennen im Orbit

6

aliasArgumente ignorieren

Manchmal möchten Sie einen Alias ​​haben, der keine Argumente enthält. Sie können es tun mit ::

> alias alert_with_args='echo hello there'

> alias alert='echo hello there;:'

> alert_with_args blabla
hello there blabla

> alert blabla
hello there

Nicht der Downvoter, aber diese Antwort zeigt :leider nicht, wie es funktioniert. Das von Ihnen angegebene Beispiel erscheint überflüssig.
Qodeninja

2
Die Frage ist nicht, wie es funktioniert, sondern was der Anwendungsfall ist . Es ist wahr, meine Antwort ist etwas ähnlich zu stackoverflow.com/a/37170755/6320039 . Aber es ist wirklich nicht der gleiche Anwendungsfall. Ich würde mich freuen, meine Antwort zu verbessern, aber ich weiß wirklich nicht, wie hier ..
Ulysse BN

1
Dies ist ein bisschen funky - ein Eckkoffer, wenn man so will - aber immer noch ziemlich interessant und könnte ein kluger Trick sein, den man in der Gesäßtasche hat.
Mike S

2
Das gleiche kann mit#
Zoey Hewll

2
@ ZoeyHewll, nicht ganz. Da Aliase vor jeder Ausführung textuell ersetzt werden , wird durch die Verwendung eines Alias #alles, was syntaktisch in derselben Zeile steht, ignoriert. Dies ist wirklich anders (überlegen Sie my_alias arg ; other_commandoder umgekehrt my_alias arg1 {BACKSLASH}{NEWLINE} arg2) und wahrscheinlich nicht das, was Sie wollen, da es zu Syntaxfehlern führen kann (raten Sie, was while my_alias ; do true ; doneoder was Sie while true ; do my_alias ; donetun werden).
Maëlan

4

Eine Verwendung sind mehrzeilige Kommentare oder das Auskommentieren eines Teils Ihres Codes zu Testzwecken, indem Sie ihn in Verbindung mit einer Here-Datei verwenden.

: << 'EOF'

This part of the script is a commented out

EOF

Vergessen Sie nicht, Anführungszeichen zu verwenden, EOFdamit der darin enthaltene Code nicht ausgewertet wird $(foo). Es könnte sich auch lohnen, ein intuitives Terminator Namen wie NOTES, SCRATCHPADoder TODO.


Cooler Trick! Besonders für Notiznotizen und Code, die Dutzende von "#" erfordern, wenn sie fest verpackt sind. Ein Nebeneffekt ist, dass einige Syntax-Textmarker im Gegensatz zu Kommentarzeilen Heredocs ignorieren und sie wie normalen Code (oder zumindest einfachen Text) färben. Dies kann ideal sein, um auskommentierten Code zu untersuchen oder Ihre Augen vor Absätzen in einer Neon-Kommentarfarbe zu schützen. Die einzige Einschränkung ist, dass solche Blöcke als Kommentare in gemeinsam genutztem Code vermerkt werden sollten, da die Syntax eindeutig ist und Verwirrung stiften kann. Andererseits handelt es sich hierbei um eine Shell, bei der das Potenzial für Verwirrung systemisch ist.
Beejor

3

Zwei von mir.

POD-Kommentare einbetten

Eine ziemlich funky Anwendung von :ist das Einbetten von POD-Kommentaren in Bash-Skripten , so dass Manpages schnell generiert werden können. Natürlich würde man irgendwann das ganze Skript in Perl umschreiben ;-)

Laufzeitfunktionsbindung

Dies ist eine Art Codemuster für Bindungsfunktionen zur Laufzeit. Fi, haben eine Debugging-Funktion, um etwas nur zu tun, wenn ein bestimmtes Flag gesetzt ist:

#!/bin/bash
# noop-demo.sh 
shopt -s expand_aliases

dbg=${DBG:-''}

function _log_dbg {
    echo >&2 "[DBG] $@"
}

log_dbg_hook=':'

[ "$dbg" ] && log_dbg_hook='_log_dbg'

alias log_dbg=$log_dbg_hook


echo "Testing noop alias..."
log_dbg 'foo' 'bar'

Du erhältst:

$ ./noop-demo.sh 
Testing noop alias...
$ DBG=1 ./noop-demo.sh 
Testing noop alias...
[DBG] foo bar

2

Manchmal können No-Op-Klauseln Ihren Code lesbarer machen.

Das kann Ansichtssache sein, aber hier ist ein Beispiel. Angenommen, Sie haben eine Funktion erstellt, die zwei Unix-Pfade verwendet. Es berechnet den 'Änderungspfad', der benötigt wird, um von einem Pfad zum anderen zu wechseln. Sie schränken Ihre Funktion ein, dass die Pfade beide mit einem '/' beginnen müssen ODER beide nicht.

function chgpath() {
    # toC, fromC are the first characters of the argument paths.
    if [[ "$toC" == / && "$fromC" == / ]] || [[ "$toC" != / && "$fromC" != / ]]
    then
        true      # continue with function
    else
        return 1  # Skip function.
    fi

Einige Entwickler werden das No-Op entfernen wollen, aber das würde bedeuten, die Bedingung zu negieren:

function chgpath() {
    # toC, fromC are the first characters of the argument paths.
    if [[ "$toC" != / || "$fromC" == / ]] && [[ "$toC" == / || "$fromC" != / ]]
    then
        return 1  # Skip function.
    fi

Nun - meiner Meinung nach - ist aus der if-Klausel nicht so klar, unter welchen Bedingungen Sie die Ausführung der Funktion überspringen möchten. Um das No-Op zu eliminieren und es klar zu machen, möchten Sie die if-Klausel aus der Funktion entfernen:

    if [[ "$toC" == / && "$fromC" == / ]] || [[ "$toC" != / && "$fromC" != / ]]
    then
        cdPath=$(chgPath pathA pathB)   # (we moved the conditional outside)

Das sieht besser aus, aber oft können wir das nicht tun. Wir möchten, dass die Prüfung innerhalb der Funktion erfolgt.

Wie oft passiert das? Nicht sehr häufig. Vielleicht ein- oder zweimal im Jahr. Es kommt oft genug vor, dass Sie sich dessen bewusst sein sollten. Ich scheue mich nicht, es zu verwenden, wenn ich denke, dass es die Lesbarkeit meines Codes verbessert (unabhängig von der Sprache).


3
Wenn Sie eine Frage über den Zweck beantworten :, sollten Sie verwenden :, nicht truein der Antwort. Der einfachste Weg, die Bedingung hier zu negieren, besteht darin, einen [[ ... ]]Befehl zu verwenden und ihm Folgendes voranzustellen !: if ! [[ ( ... && ... ) || ( ... && ... ) ]]; then.
Chepper

1
Ah, sehr nett, und ich sollte meine Antwort ablehnen. Hmmm ... Ich denke immer noch an Beispiele, bei denen ich eine No-Op-Klausel verwenden musste. Dies ist das Beste, was ich mir ausgedacht habe.
Bitdiot

Es wird wirklich nur aus Gründen der Abwärtskompatibilität beibehalten, obwohl : ${...=...}es wohl weniger umständlich aussieht als true ${...=...}. Einige bevorzugen while :; does vielleicht auch while true; dowegen der Knappheit.
Chepper

2

Etwas im Zusammenhang mit dieser Antwort finde ich dieses No-Op ziemlich praktisch, um polyglotte Skripte zu hacken . Hier ist zum Beispiel ein gültiger Kommentar sowohl für Bash als auch für Vimscript:

":" #    this is a comment
":" #    in bash, ‘:’ is a no-op and ‘#’ starts a comment line
":" #    in vimscript, ‘"’ starts a comment line

Sicher, wir haben es vielleicht truegenauso gut verwendet, aber :ein Interpunktionszeichen und kein irrelevantes englisches Wort zu sein, macht deutlich, dass es sich um ein Syntax-Token handelt.


Was , warum sollte jemand tun , eine solche heikle Sache wie eine polyglotte Skript zu schreiben (außer es ist cool): beweist es hilfreich in Situationen , in denen wir normalerweise mehrere Skriptdateien in verschiedenen Sprachen schreiben würde, mit Datei Xzu Datei beziehen Y.

In einer solchen Situation vermeidet das Kombinieren beider Skripte in einer einzigen polyglotten Datei jegliche Arbeit bei der XBestimmung des Pfades zu Y(es ist einfach "$0"). Noch wichtiger ist, dass es bequemer ist, sich im Programm zu bewegen oder es zu verteilen.

  • Ein häufiges Beispiel. Es gibt ein bekanntes, seit langem bestehendes Problem mit Shebangs: Bei den meisten Systemen (einschließlich Linux und Cygwin) kann nur ein Argument an den Interpreter übergeben werden. Der folgende Schebang:

    #!/usr/bin/env interpreter --load-libA --load-libB

    wird den folgenden Befehl auslösen:

    /usr/bin/env "interpreter --load-libA --load-libB" "/path/to/script"

    und nicht die beabsichtigte:

    /usr/bin/env interpreter --load-libA --load-libB "/path/to/script"

    Sie würden also am Ende ein Wrapper-Skript schreiben, wie zum Beispiel:

    #!/usr/bin/env sh
    /usr/bin/env interpreter --load-libA --load-libB "/path/to/script"

    Hier kommt die Polyglossie ins Spiel.

  • Ein genaueres Beispiel. Ich habe einmal ein Bash-Skript geschrieben, das unter anderem Vim aufrief. Ich musste Vim zusätzliches Setup geben, was mit der Option möglich war --cmd "arbitrary vimscript command here". Dieses Setup war jedoch umfangreich, so dass es schrecklich gewesen wäre, es in eine Zeichenfolge einzufügen (wenn es jemals möglich gewesen wäre). Daher war es eine bessere Lösung, es in einer Konfigurationsdatei in extenso zu schreiben und dann Vim diese Datei mit lesen zu lassen -S "/path/to/file". Daher habe ich eine polyglotte Bash / Vimscript-Datei erhalten.


1

Ich habe auch darin Skripte verwendet, um Standardvariablen zu definieren.


: ${VARIABLE1:=my_default_value}
: ${VARIABLE2:=other_default_value}
call-my-script ${VARIABLE1} ${VARIABLE2}

0

Angenommen, Sie haben einen Befehl, den Sie zum Erfolg eines anderen verketten möchten:

cmd="some command..."
$cmd
[ $? -eq 0 ] && some-other-command

Jetzt möchten Sie die Befehle jedoch bedingt ausführen und die Befehle anzeigen, die ausgeführt werden sollen (Trockenlauf):

cmd="some command..."
[ ! -z "$DEBUG" ] && echo $cmd
[ -z "$NOEXEC" ] && $cmd
[ $? -eq 0 ] && {
    cmd="some-other-command"
    [ ! -z "$DEBUG" ] && echo $cmd
    [ -z "$NOEXEC" ] && $cmd
}

Wenn Sie also DEBUG und NOEXEC festlegen, wird der zweite Befehl nie angezeigt. Dies liegt daran, dass der erste Befehl niemals ausgeführt wird (weil NOEXEC nicht leer ist), aber die Auswertung dieser Tatsache führt zu einer Rückgabe von 1, was bedeutet, dass der untergeordnete Befehl niemals ausgeführt wird (aber Sie möchten, dass es sich um einen Trockenlauf handelt). Um dies zu beheben, können Sie den auf dem Stapel verbleibenden Exit-Wert mit einem Noop zurücksetzen:

[ -z "$NOEXEC" ] && $cmd || :
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.