Qual é a utilidade do comando: no script de shell, já que ele explicitamente não faz nada?

27

Na resposta a esta pergunta sobre comentários em scripts de shell , é indicado que :é um comando nulo que explicitamente não faz nada (mas não deve ser usado para comentários).

Qual seria a utilidade de um comando que não faz absolutamente nada?

DQdlM
fonte
11
Consulte também A que finalidade serve o cólon interno .
jw013
2
Veja também esta pergunta que tem uma resposta ainda melhor aqui , ou seja, que :é necessário para ser um built-in, enquanto truenão é, o que afeta o escopo de variáveis
Old Pro
2
Ele explicitamente não faz nada graciosamente .
Mikeerv

Respostas:

19

Eu normalmente uso trueem loops; Eu acho que é mais claro:

while true; do
    ...
done

O único lugar que achei :realmente útil é o caso, se você precisar corresponder a algo, mas não quiser realmente fazer nada. Por exemplo:

case $answer in
    ([Yy]*) : ok ;;
    (*)     echo "stop."; exit 1 ;;
esac
Cientista maluco
fonte
3
Concordo. truepara uma condição, :para um NOP.
Jw013
11
o bash aceita case a in a ) ;; esac. Existem algumas conchas que não aceitam isso?
Kaz
@Kaz: According to the POSIX shell grammar, case ${var} in value);; *) do_something;; esac is acceptable. The : command is not needed for empty cases.
Richard Hansen
12

Originalmente, era usado para determinar que era um programa shell Bourne, em oposição ao programa compilado em C. Isso foi antes do shebang e de várias linguagens de script (csh, perl). Você ainda pode executar um script começando com apenas ::

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

Geralmente, ele executa o script contra $SHELL(ou /bin/sh).

Desde então, o principal uso é avaliar os argumentos. Eu ainda uso:

: ${EDITOR:=vim}

para definir um valor padrão em um script.

Arcege
fonte
11

: é útil para gravar loops que devem ser finalizados de dentro.

while :
do
    ...stuff...
done

Isto irá executar sempre a menos breakou exité chamado, ou o shell recebe um sinal de terminação.

Kyle Jones
fonte
3
Eu sinto que while true; do ...; doneComunica intenções para o leitor melhor do quewhile :; do ...; done
Richard Hansen
9

Quando você deseja uma instrução "a menos que" no script de shell, usa uma condição "não", que pode parecer ridícula para alguns dos testes ou usa ':' na cláusula true, com código real no false cláusula.

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

A "condição exótica" pode ser algo que você não deseja negar, ou isso é muito mais claro se você não usar "lógica negativa".

Bruce Ediger
fonte
11
Ele também aparece nos scripts gerados por, autoconfporque é muito mais fácil adicionar um padrão :para ramificações vazias do que descobrir como reverter a condição.
Dietrich Epp
2
Não consigo ver como ficar furioso !diante de [ some-exotic-condition ]um pateta, mas um supérfluo : elsedepois dele não é pateta.
Kaz
@Kaz tem um bom argumento. Lembre-se de que é difícil criar condições exóticas. Se você precisar negar tudo, isso é uma coisa, mas isso pode deixar a condição menos clara. Faz um '!' negar toda a condição ou apenas o primeiro termo? É melhor ter uma cláusula ':' true às vezes.
Bruce Ediger
O !token nega todo um elemento do tubo de comando. while ! grep ... ; do ... done ou if ! [ ... ] ; then ... fi. É basicamente externo à test/[]sintaxe. Veja: pubs.opengroup.org/onlinepubs/9699919799/utilities/…
Kaz
5

Eu só usei isso além do caractere # para comentar temporariamente uma linha, em uma situação em que comentar a linha produz um erro de sintaxe, devido a um defeito na gramática do shell de não permitir uma sequência vazia de comandos :

if condition ; then
    :# temporarily commented out command
fi

Sem o comando: falta uma sequência de comandos, que é um erro de sintaxe.

Kaz
fonte
4

Há dois casos em que acho :útil:

Atribuições de variável padrão

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

Essa é uma maneira conveniente de permitir que os usuários do seu script shell substituam uma configuração sem editar o script. (No entanto, argumentos de linha de comando são melhores porque você não corre o risco de comportamento inesperado se o usuário tiver coincidentemente a variável que você usa no ambiente exportado.) Veja como o usuário substituirá a configuração:

VAR="other value" ./script

A ${VAR=value}sintaxe diz ao set VARpara valueseVAR já não estiver definida e, em seguida, expanda para o valor da variável. Como ainda não nos importamos com o valor da variável, ela é passada como argumento ao comando no-op :para jogá-la fora.

Mesmo sendo :um comando não operacional, a expansão é realizada pelo shell (não pelo: comando!) Antes de executar o comando: comando, para que a atribuição de variável ainda ocorra (se aplicável).

Também seria aceitável usar trueou algum outro comando em vez de: , mas o código se torna mais difícil de ler porque a intenção é menos clara.

O script a seguir também funcionaria:

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

Mas o acima é muito mais difícil de manter. Se uma linha usando ${VAR}for adicionada acima dessa printflinha, a expansão da atribuição padrão deverá ser movida. Se o desenvolvedor esquecer de mover essa atribuição, um bug será introduzido.

Algo para colocar em um bloco condicional vazio

Blocos condicionais vazios geralmente devem ser evitados, mas às vezes são úteis:

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

Algumas pessoas argumentam que ter um ifbloco verdadeiro vazio pode facilitar a leitura do código do que negar o teste. Por exemplo:

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

é sem dúvida mais fácil de ler do que:

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

No entanto, acredito que existem algumas abordagens alternativas que são melhores do que um verdadeiro bloco vazio:

  1. Coloque a condição em uma função:

    exotic_condition() { [ -f foo ] && bar || baz; }
    
    if ! exotic_condition; then
        do_something_here
    fi
  2. Coloque a condição dentro de chaves (ou parênteses, mas os parênteses geram um processo de subcamação e todas as alterações feitas no ambiente dentro da subcamada não serão visíveis fora dela) antes de negar:

    if ! { [ -f foo ] && bar || baz; } then
        do_something_here
    fi
  3. Use em ||vez de if:

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

    Eu prefiro essa abordagem quando a reação é simples, como afirmar condições:

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

No antigo shell pré-bourne nas versões antigas do UNIX, o :comando era originalmente destinado a especificar rótulos para goto(era um comando separado que direciona a entrada para onde o rótulo é encontrado, para que rótulos não pudessem ser uma sintaxe separada que o O shell sabe sobre o. iftambém foi um comando separado.) Logo se tornou usado para comentários, antes que houvesse uma sintaxe de comentário ( #era usada para backspace) e hoje em dia existe compatibilidade tanto quanto qualquer outra coisa.

Random832
fonte
0

Além de usá-lo como uma declaração que não faz nada, você pode usá-lo para comentar declarações únicas, transformando-as em argumentos para:.

Martin Stein
fonte
Não será exatamente um comentário, pois : echo write this line > myfileainda criará um arquivo vazio.
Arcege
5
Conforme explicado no link da pergunta, NÃO: é um mecanismo de comentário adequado.
Jw013