Jaki jest przypadek użycia noop [:] w bash?


124

Szukałem noop w bash (:), ale nie mogłem znaleźć żadnych dobrych informacji. Jaki jest dokładny cel lub przypadek użycia tego operatora?

Próbowałem śledzić i działa to tak dla mnie:

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

Daj mi znać, każdy przypadek użycia tego operatora w czasie rzeczywistym lub w dowolnym miejscu, w którym jest to obowiązkowe.



Zauważ, że funkcja :wbudowana istnieje w powłoce burne i ksh oraz bash.
ghoti


6
Dlaczego to nie jest prawdziwe pytanie? Myślę, że to naprawdę dobre pytanie. Nawet mam z tego dobry użytek, ale nie mogę opublikować odpowiedzi.
Steven Lu

Odpowiedzi:


176

Jest tam więcej z powodów historycznych. Wbudowany dwukropek :jest dokładnie równoważny z true. Jest to tradycyjne, truegdy wartość zwracana jest ważna, na przykład w nieskończonej pętli:

while true; do
  echo 'Going on forever'
done

Jest to tradycyjne w użyciu, :gdy składnia powłoki wymaga polecenia, ale nie masz nic do roboty.

while keep_waiting; do
  : # busy-wait
done

Te :terminy wbudowane wszystkie drodze powrotnej do Thompson skorupy , to był obecny w Unix v6 . :był wskaźnikiem etykiety dla stwierdzenia powłoki Thompsona goto. Etykieta może być dowolnym tekstem, więc :podwojona jako wskaźnik komentarza (jeśli nie ma goto comment, : commentto faktycznie komentarz). Bourne shell nie miał gotojednak utrzymane: .

Popularny idiom, który używa :is : ${var=VALUE}, który ustawia varsię, VALUEjeśli był nieustawiony i nic nie robi, jeśli varbył już ustawiony. Konstrukcja ta istnieje tylko w postaci podstawiania zmiennych i to podstawianie zmiennych musi w jakiś sposób stanowić część polecenia: polecenie no-op dobrze służy.

Zobacz także Czemu służy wbudowany dwukropek? .


11
Dobre podsumowanie. Ponadto oryginalna powłoka Bourne'a nie używała #do komentarzy (ani #!/bin/shszarpnięć); :polecenia wprowadzone komentarze i biada naiwnego programisty, który dokonał pole piękny gwiazd w uwagach: : ****(lub, co gorsza, : * * *).
Jonathan Leffler

3
@JonathanLeffler: Wstyd mi, ale nie rozumiem. Co by się stało : * * *?
DevSolar

7
Ponieważ :jest to polecenie, powłoka nadal musi przetworzyć swoje argumenty, zanim odkryje, że :je ignoruje. W większości przypadków po prostu zmuszasz powłokę do wykonania dodatkowej pracy przy rozszerzaniu *listy plików w bieżącym katalogu; w rzeczywistości nie wpłynie to na sposób działania skryptu.
chepner

Przypuszczam, że może to spowodować niepowodzenie skryptu, jeśli zdarzy ci się uruchomić w katalogu z dużą ilością plików, co spowoduje, że rozszerzenie globu przekroczy limit długości polecenia? Może tylko jeśli masz set -ena sobie. W każdym razie, miejmy nadzieję, że wszyscy zgodzimy się na użycie #:)
Jack O'Connor

2
Czasami używam, while : ; do ... ; donejeśli chcę szybką i brudną nieskończoną pętlę. (Zwykle sleepw pętli jest i naciskam Ctrl-C, aby go zabić.)
Keith Thompson

21

Używam go do instrukcji if, kiedy komentuję cały kod. Na przykład masz test:

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

ale chcesz tymczasowo wykomentować wszystko zawarte w:

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

Co powoduje, że bash podaje błąd składniowy:

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

Bash nie może mieć pustych bloków (WTF). Więc dodajesz no-op:

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

lub możesz użyć opcji no-op, aby skomentować linie:

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

12

Jeśli używasz set- eto, || :jest to świetny sposób, aby nie wychodzić ze skryptu, jeśli wystąpi awaria (jawnie sprawia, że ​​przechodzi).


1
Uważam, że jest to przydatne w przypadku niektórych aplikacji wywołujących skrypty powłoki. Na przykład Automator na Macu zakończy działanie po błędzie i nie powie Ci dlaczego. Jednak || :późniejsze skorzystanie z samodzielnej obsługi błędu za pomocą polecenia an exit 0pozwoli wyświetlić wszystkie standardowe wyjścia i błędy w oknie aplikacji podczas debugowania. Zastanów się, { foo ... 2>&1; } || :co jest znacznie prostsze niż tworzenie bardziej złożonych pułapek i opcji „jeśli-to”.
Beejor

7

Możesz użyć :polecenia, które się powiedzie, ale nic nie zrobi. W tym przykładzie polecenie „gadatliwość” jest domyślnie wyłączone, ustawiając je na :. Opcja „v” ją włącza.

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

mówiąc ściślej, przypisanie wartości do zmiennej jest „robieniem czegoś”, dlatego nie powoduje błędu w instrukcji case.
qodeninja

@qodeninja: Nikt nie twierdził, że przypisanie zmiennej „nic nie robi”; chodzi o to, że $(verbosity $i)późniejszy (i warunkowo) nic nie robi.
Wyścigi lekkości na orbicie

6

Ignorować alias argumenty

Czasami chcesz mieć alias, który nie przyjmuje żadnego argumentu. Możesz to zrobić za pomocą ::

> alias alert_with_args='echo hello there'

> alias alert='echo hello there;:'

> alert_with_args blabla
hello there blabla

> alert blabla
hello there

nie przeciwnik, ale ta odpowiedź :niestety nie pokazuje, jak działa. Podany przykład wydaje się zbędny.
qodeninja

2
Pytanie nie brzmi, jak to działa, ale jaki jest przypadek użycia . To prawda, że ​​moja odpowiedź jest trochę podobna do stackoverflow.com/a/37170755/6320039 . Ale tak naprawdę nie jest to ten sam przypadek użycia. Chętnie poprawię swoją odpowiedź, ale naprawdę nie wiem, jak tutaj ..
Ulysse BN,

1
To trochę fajne - jeśli wolisz narożnik - ale wciąż całkiem interesujące i może być sprytnym trikiem, który warto mieć w tylnej kieszeni.
Mike S

2
To samo można osiągnąć dzięki#
Zoey Hewll

2
@ZoeyHewll, niezupełnie. Ponieważ aliasy są podstawiane tekstowo przed jakimkolwiek wykonaniem, użycie aliasu do #spowodowałoby, że wszystko, co pojawia się syntaktycznie po tej samej linii, byłoby ignorowane. Jest to naprawdę inne (rozważ my_alias arg ; other_commandlub odwrotnie my_alias arg1 {BACKSLASH}{NEWLINE} arg2) i prawdopodobnie nie jest tym, czego chcesz, ponieważ może powodować błędy składniowe (zgadnij, co zrobi while my_alias ; do true ; donelub while true ; do my_alias ; donezrobi).
Maëlan

4

Jednym z zastosowań są komentarze wielowierszowe lub skomentowanie części kodu do celów testowych, używając go w połączeniu z plikiem tutaj.

: << 'EOF'

This part of the script is a commented out

EOF

Nie zapomnij użyć cudzysłowów EOF, aby żaden kod wewnątrz nie został oceniony, na przykład$(foo) . To również może być warta używając intuicyjnego nazwę terminatora jak NOTES, SCRATCHPADlub TODO.


Fajna sztuczka! Szczególnie w przypadku notatek i kodu wymagającego dziesiątek "#" po zapakowaniu na sztywno. Efektem ubocznym jest to, że w przeciwieństwie do wierszy komentarzy, niektóre podświetlacze składni ignorują heredoc, kolorując je jak normalny kod (lub przynajmniej zwykły tekst). Co może być świetne do zbadania skomentowanego kodu lub uratowania oczu przed akapitami w neonowym kolorze komentarzy. Jedynym zastrzeżeniem jest to, że takie bloki należy odnotować jako komentarze w kodzie współdzielonym, ponieważ składnia jest unikalna i może powodować zamieszanie. Z drugiej strony jest to powłoka, o której mówimy, w której potencjalne pomyłki są systemowe.
Beejor

3

Moje dwa.

Osadzaj komentarze POD

Całkiem fajna aplikacja :służy do osadzania komentarzy POD w skryptach bash , dzięki czemu strony podręcznika mogą być szybko generowane. Oczywiście można by ostatecznie przepisać cały skrypt w Perlu ;-)

Powiązanie funkcji czasu wykonywania

Jest to rodzaj wzorca kodu dla funkcji wiążących w czasie wykonywania. Fi, miej funkcję debugowania, aby zrobić coś tylko wtedy, gdy jest ustawiona określona flaga:

#!/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'

Dostajesz:

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

2

Czasami klauzule no-op mogą sprawić, że kod będzie bardziej czytelny.

To może być kwestia opinii, ale oto przykład. Załóżmy, że utworzyłeś funkcję, która działa, biorąc dwie ścieżki uniksowe. Oblicza „ścieżkę zmiany” potrzebną do przejścia z jednej ścieżki do drugiej. Na swoją funkcję nakładasz ograniczenie, że obie ścieżki muszą zaczynać się od znaku „/” LUB oba nie mogą.

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

Niektórzy programiści będą chcieli usunąć opcję no-op, ale oznaczałoby to zanegowanie warunku:

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

Teraz - moim zdaniem - z klauzuli if nie wynika jasno warunki, w których chciałbyś pominąć wykonywanie funkcji. Aby wyeliminować no-op i zrobić to wyraźnie, chciałbyś usunąć klauzulę if z funkcji:

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

Wygląda to lepiej, ale wiele razy nie możemy tego zrobić; chcemy, aby sprawdzanie było wykonywane wewnątrz funkcji.

Jak często to się dzieje? Niezbyt często. Może raz lub dwa razy w roku. Dość często zdarza się, że warto być tego świadomym. Nie boję się go używać, gdy myślę, że poprawia czytelność mojego kodu (niezależnie od języka).


3
Jeśli odpowiedzi na pytanie o celu :należy użyć :, a nie truew odpowiedzi. Powiedział, że najprostszym sposobem, aby negować tutaj warunkowego jest użycie jednego [[ ... ]]polecenia i poprzedzić go z !: if ! [[ ( ... && ... ) || ( ... && ... ) ]]; then.
chepner

1
Ach, bardzo miło i powinienem odmówić głosowania. Hmmm ... Nadal myślę o przykładach, w których musiałem użyć klauzuli no-op. To najlepsze, co wymyśliłem.
Bitdiot

Jest tak naprawdę zachowany w celu zapewnienia kompatybilności wstecznej, chociaż : ${...=...}prawdopodobnie wygląda mniej niezręcznie niż true ${...=...}. Niektórzy wolą while :; doteż while true; dozwięzłość.
chepner

2

Trochę związane z tą odpowiedzią , uważam, że ten nie-op raczej wygodny do hakowania skryptów poligloty . Na przykład, tutaj jest poprawny komentarz zarówno dla basha, jak i dla vimscript:

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

Jasne, mogliśmy truerównie dobrze użyć , ale :będąc znakiem interpunkcyjnym, a nie nieistotnym angielskim słowem, jasno widać, że jest to token składni.


Jeśli chodzi o tego , dlaczego ktoś miałby zrobić tak trudną rzecz, jak napisanie skryptu poliglotowego (poza tym, że jest fajny): okazuje się pomocny w sytuacjach, w których normalnie pisalibyśmy kilka plików skryptów w kilku różnych językach, z plikiem Xodnoszącym się do pliku Y.

W takiej sytuacji połączenie obu skryptów w jednym pliku poliglotowym pozwala uniknąć pracy w X przy określaniu ścieżki do Y(po prostu "$0"). Co ważniejsze, ułatwia to poruszanie się po programie lub jego dystrybucję.

  • Typowy przykład.Istnieje dobrze znany od dawna problem z shebangiem: większość systemów (w tym Linux i Cygwin) zezwala na przekazanie tłumaczowi tylko jednego argumentu. Następujące shebang:

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

    uruchomi następujące polecenie:

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

    a nie zamierzone:

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

    W rezultacie napisałbyś skrypt opakowujący, taki jak:

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

    I tu na scenę wkracza poliglossia.

  • Bardziej konkretny przykład. Kiedyś napisałem skrypt basha, który między innymi wywołał Vima. Musiałem dać Vimowi dodatkową konfigurację, co można by zrobić za pomocą tej opcji --cmd "arbitrary vimscript command here". Jednak ta konfiguracja była znaczna, więc umieszczenie jej w łańcuchu byłoby okropne (jeśli kiedykolwiek było to możliwe). Dlatego lepszym rozwiązaniem było zapisanie go w extenso w jakimś pliku konfiguracyjnym, a następnie zmusić Vima do odczytania tego pliku za pomocą -S "/path/to/file". W rezultacie otrzymałem plik polyglot bash / vimscript.


1

Użyłem w nim również skryptów do definiowania domyślnych zmiennych.


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

0

załóżmy, że masz polecenie, które chcesz powiązać z sukcesem innego:

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

ale teraz chcesz wykonać polecenia warunkowo i chcesz wyświetlić polecenia, które zostaną wykonane (przebieg na sucho):

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

więc jeśli ustawisz DEBUG i NOEXEC, drugie polecenie nigdy się nie pojawi. dzieje się tak, ponieważ pierwsza komenda nigdy nie jest wykonywana (ponieważ NOEXEC nie jest pusty), ale ocena tego faktu pozostawia zwrot 1, co oznacza, że ​​polecenie podrzędne nigdy nie jest wykonywane (ale chcesz, aby było to robione na sucho). więc aby to naprawić, możesz zresetować wartość wyjściową pozostawioną na stosie za pomocą noop:

[ -z "$NOEXEC" ] && $cmd || :
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.