Jaka jest użyteczność polecenia: w skryptach powłoki, biorąc pod uwagę, że jawnie nic nie robi?

27

W odpowiedzi na to pytanie dotyczące komentarzy w skryptach powłoki wskazano, że :jest to polecenie zerowe, które jawnie nic nie robi (ale nie powinno być używane do komentarzy).

Jaka byłaby użyteczność polecenia, które absolutnie nic nie robi?

DQdlM
źródło
1
Zobacz także Jaki jest cel wbudowanego dwukropka .
jw013
2
Zobacz także to pytanie, które ma jeszcze lepszą odpowiedź tutaj , a mianowicie :to, że musi być wbudowane, a truenie jest, co ma wpływ na zakres zmiennych
Old Pro
2
To nie robi nic z gracją .
mikeserv

Odpowiedzi:

19

Zwykle używam truew pętlach; Myślę, że to bardziej jasne:

while true; do
    ...
done

Jedyne miejsce, które znalazłem, które :jest naprawdę przydatne, to instrukcje na wypadek, gdybyś chciał coś dopasować, ale nie chcesz nic robić. Na przykład:

case $answer in
    ([Yy]*) : ok ;;
    (*)     echo "stop."; exit 1 ;;
esac
Szalony naukowiec
źródło
3
Zgadzam się. truedla warunku, :dla NOP.
jw013
1
bash akceptuje case a in a ) ;; esac. Czy są jakieś powłoki, które tego nie akceptują?
Kaz
@Kaz: Zgodnie z gramatyki POSIX powłoki , case ${var} in value);; *) do_something;; esacjest do zaakceptowania. :Komenda nie jest potrzebny do pustych przypadkach.
Richard Hansen
12

Pierwotnie był używany do ustalenia, że ​​był to program powłoki Bourne'a, w przeciwieństwie do programu skompilowanego w języku C. Było to przed shebangiem i wieloma językami skryptowymi (csh, perl). Nadal możesz uruchomić skrypt, zaczynając od ::

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

Generalnie uruchamia skrypt przeciwko $SHELL(lub /bin/sh).

Od tego czasu głównym zastosowaniem jest ocena argumentów. Nadal używam:

: ${EDITOR:=vim}

ustawić wartość domyślną w skrypcie.

Arcege
źródło
11

: jest przydatny do pisania pętli, które muszą być zakończone od wewnątrz.

while :
do
    ...stuff...
done

Będzie to działać na zawsze, chyba że breakalbo exitjest nazywany lub powłoka otrzyma sygnał obciążeniowy.

Kyle Jones
źródło
3
Wydaje mi się, że lepiej while true; do ...; doneprzekazuje intencje czytelnikowi niżwhile :; do ...; done
Richard Hansen
9

Jeśli potrzebujesz skryptu powłoki w instrukcji „chyba”, możesz albo użyć warunku „nie”, który może wyglądać głupio w przypadku niektórych testów, albo użyć „:” w klauzuli true, z prawdziwym kodem w false- klauzula.

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

„Egzotyczny warunek” może być czymś, czego nie chcesz negować, lub jest to o wiele wyraźniejsze, jeśli nie używasz „logiki negatywnej”.

Bruce Ediger
źródło
1
Pokazuje się również w wygenerowanych skryptach, autoconfponieważ o wiele łatwiej jest dodać domyślną wartość :dla pustych gałęzi, niż dowiedzieć się, jak odwrócić ten warunek.
Dietrich Epp
2
Nie widzę, jak trzymanie się !z przodu [ some-exotic-condition ]jest głupie, ale zbędne : elsepo nim nie jest głupie.
Kaz
@Kaz ma dobrą rację. Pamiętajmy, że wymyślanie egzotycznych warunków jest w najlepszym wypadku trudne. Jeśli musisz to wszystko zanegować, to jedno, ale może to po prostu uczynić ten warunek mniej wyraźnym. Czy „!” negować cały warunek, czy tylko pierwszy termin? Najlepiej czasami mieć prawdziwą klauzulę „:”.
Bruce Ediger
!Znacznik neguje całego elementu rurowego poleceń. while ! grep ... ; do ... done lub if ! [ ... ] ; then ... fi. Jest to w zasadzie zewnętrzne względem test/[]składni. Zobacz: pubs.opengroup.org/onlinepubs/9699919799/utilities/…
Kaz
5

Użyłem tego oprócz znaku # do tymczasowego komentowania linii, w sytuacji, gdy komentowanie linii powoduje błąd składniowy, z powodu wady gramatyki powłoki polegającej na niedopuszczeniu pustej sekwencji poleceń :

if condition ; then
    :# temporarily commented out command
fi

Bez: mamy brakującą sekwencję poleceń, która jest błędem składni.

Kaz
źródło
4

Przydają mi się dwa przypadki ::

Domyślne przypisania zmiennych

#!/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}"

Jest to wygodny sposób na zezwolenie użytkownikom skryptu powłoki na zastąpienie ustawienia bez edytowania skryptu. (Jednak argumenty wiersza polecenia są lepsze, ponieważ nie ryzykujesz nieoczekiwanym zachowaniem, jeśli użytkownik przypadkowo ma zmienną, której używasz w wyeksportowanym środowisku). Oto, w jaki sposób użytkownik zastąpi to ustawienie:

VAR="other value" ./script

${VAR=value}Składnia mówi do zestawu VAR, aby valuejeśli VARnie jest już ustawiony, a następnie rozszerzyć do wartości zmiennej. Ponieważ nie zależy nam jeszcze na wartości zmiennej, jest ona przekazywana jako argument do polecenia no-op, :aby ją wyrzucić.

Mimo że polecenie :to nie działa, interpretacja jest wykonywana przez powłokę (nie :polecenie!) Przed uruchomieniem :polecenia, więc przypisanie zmiennej nadal występuje (jeśli dotyczy).

Dopuszczalne byłoby również użycie truelub użycie innego polecenia :, ale kod staje się trudniejszy do odczytania, ponieważ zamiar jest mniej jasny.

Działa również następujący skrypt:

#!/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}"

Ale powyższe jest znacznie trudniejsze do utrzymania. Jeśli ${VAR}powyżej linii zostanie dodana linia używająca, printfnależy przenieść domyślne rozszerzenie przypisania. Jeśli programista zapomni przenieść to zadanie, zostanie wprowadzony błąd.

Coś do umieszczenia w pustym bloku warunkowym

Ogólnie należy unikać pustych bloków warunkowych, ale czasem są one przydatne:

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

Niektórzy twierdzą, że posiadanie pustego prawdziwego ifbloku może ułatwić odczytanie kodu niż negowanie testu. Na przykład:

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

jest prawdopodobnie łatwiejszy do odczytania niż:

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

Jednak uważam, że istnieje kilka alternatywnych metod, które są lepsze niż pusty prawdziwy blok:

  1. Umieść warunek w funkcji:

    exotic_condition() { [ -f foo ] && bar || baz; }
    
    if ! exotic_condition; then
        do_something_here
    fi
    
  2. Umieść warunek w nawiasach klamrowych (lub nawiasach, ale nawiasy spawnują proces podpowłoki, a wszelkie zmiany dokonane w środowisku w podpowłoce nie będą widoczne poza podpowłoką) przed zanegowaniem:

    if ! { [ -f foo ] && bar || baz; } then
        do_something_here
    fi
    
  3. Użyj ||zamiast if:

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

    Wolę to podejście, gdy reakcja jest prostą jednowarstwową, taką jak zapewnienie warunków:

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

W starej powłoce pre-bourne w starożytnych wersjach systemu UNIX :komenda pierwotnie była przeznaczona do określania etykiet dla goto(była to osobna komenda, która nakierowuje dane wejściowe do miejsca znalezienia etykiety, więc etykiety nie mogą być osobną składnią, że shell wie, że. ifbył również osobnym poleceniem.) Wkrótce został użyty do komentarzy, zanim pojawiła się składnia komentarza ( #została użyta do cofania) i obecnie jest około kompatybilności.

Losowo 832
źródło
0

Oprócz używania go jako instrukcji, która nic nie robi, możesz go używać do komentowania pojedynczych instrukcji, zamieniając je w argumenty dla:.

Martin Stein
źródło
To nie będzie dokładnie komentarz, ponieważ : echo write this line > myfilenadal utworzy pusty plik.
Arcege
5
Jak wyjaśniono w link zawarty w pytaniu, :jest nie odpowiedni mechanizm komentowania.
jw013