comando bash multi line com comentários após o caractere de continuação

27

Considere

echo \ # this is a comment
foo

Isso dá:

$ sh foo.sh
 # this is a comment
foo.sh: line 2: foo: command not found

Após algumas pesquisas na Web, encontrei uma solução da DigitalRoss no site da irmã Stack Overflow. Então pode-se fazer

echo ': this is a comment' \
foo

ou alternativamente

echo $(: this is a comment) \
foo

No entanto, a DigitalRoss não explicou por que essas soluções funcionam. Eu apreciaria uma explicação. Ele respondeu com um comentário:

There used to be a shell goto command which branched to labels specified like : here. The goto is gone but you can still use the : whatever syntax ... : is a sort of parsed comment now.

Mas gostaria de mais detalhes e contexto, incluindo uma discussão sobre portabilidade.

Claro, se alguém tiver outras soluções, isso também seria bom.

Veja também a pergunta anterior Como comentar multi comandos de linha em scripts de shell? .

Leve para casa a mensagem da discussão abaixo. O ': this is a comment' é apenas uma substituição de comando. A saída de : this is a comment não é nada e que é colocado no lugar de ': this is a comment' .

Uma melhor escolha é a seguinte:

echo '# this is a comment' \
foo
    
por Faheem Mitha 21.08.2011 / 17:14

3 respostas

21

Os comentários terminam na primeira nova linha (consulte regra de reconhecimento de token de shell 10 ), sem permitir linhas de continuação , então esse código tem foo em uma linha de comando separada:

echo # this is a comment \
foo

Quanto à sua primeira proposta, a barra invertida não é seguida por uma nova linha, você está apenas citando o espaço: é equivalente a

echo ' # this is a comment'
foo

$(: this is a comment) substitui a saída do comando : this is a comment . Se a saída desse comando estiver vazia, isso é efetivamente uma maneira altamente confusa de inserir um comentário no meio de uma linha.

Não há mágica acontecendo: : é um comando comum, o utilitário cólon que não faz nada. O utilitário de dois pontos é mais útil quando a sintaxe do shell requer um comando, mas você não tem nada para fazer.

# Sample code to compress files that don't look compressed
case "$1" in
  *.gz|*.tgz|*.bz2|*.zip|*.jar|*.od?) :;; # the file is already compressed
  *) bzip2 -9 "$1";;
esac

Outro caso de uso é um idioma para definir uma variável, se ela ainda não estiver definida.

: "${foo:=default value}"

A observação sobre o goto é histórica. O utilitário de cólon é datado antes mesmo do Bourne shell , até o Bomba do Thompson , que tinha uma instrução ir . O cólon então significava um rótulo; dois-pontos é uma sintaxe bastante comum para rótulos goto (ainda está presente em sed ).

    
por 21.08.2011 / 17:36
5

Você pode conseguir isso usando matrizes Bash, por exemplo

#!/bin/bash
CMD=(
  echo  # this is a comment
  foo
  )

"${CMD[@]}"

Isso define uma matriz, $CMD , e depois a expande. Uma vez expandida, a linha resultante é avaliada, portanto, neste caso, echo foo é executado.

O texto entre ( e ) define a matriz e está sujeito à sintaxe bash usual, então tudo em uma linha após # é ignorado.

Nota sobre como preservar espaços em branco entre aspas

${CMD[@]} expande para uma única string que é a concatenação de todos os elementos, separados por um espaço. Depois de expandido, o Bash analisaria a sequência em tokens da maneira usual (cf $ IFS ) , que muitas vezes não é o que queremos.

Por outro lado, se a expansão estiver entre aspas duplas, ou seja, "${CMD[@]}" , cada elemento da matriz será preservado. Considere a diferença entre hello world second item e "hello world" "second item" .

Exemplo ilustrativo:

# LIST=("hello world" "second item")

# for ITEM in ${LIST[@]}; do echo $ITEM; done
hello
world
second
item

# for ITEM in "${LIST[@]}"; do echo $ITEM; done
hello world
second item
    
por 05.04.2015 / 18:45
4

Não faça $(: comment) . Isso não é um comentário - isso é um subshell - outro processo de shell totalmente novo para a maioria dos shells. Seu objetivo é fazer menos w / sua entrada, não mais, que é o que isso faria - mesmo que seja inútil.

Você pode fazer ...

printf '<%s>\n' some args here ${-##*"${--

                my long comment block

                }"}  and "more ${-##*"${--

                and another one in the
                middle of the quoted string
                there shouldn\'t b\e any special &
                (character) 'echo issues here i hope >&2'
                basically anything that isn\'t a close \}
                $(echo the shell is looking for one >&2)
                }$(echo "}'"\" need backslash escaping >&2
                                )${-##*${--

                nesting is cool though

             }}"}here too"
}'" need backslash escaping
<some>
<args>
<here>
<and>
<more here too>

Basicamente, o que está acontecendo é que o shell está fazendo uma substituição. Ele substitui o valor do parâmetro de shell especial $- duas vezes a cada vez. É uma string curta de qualquer forma, mas é sempre definida - e assim a substituição interna - que é interpretada como um padrão para remover do exterior - não expande para o conteúdo entre parênteses quando eu uso o formulário de expansão - .

Aqui:

bash -x <<""
printf %s\n '${-##*"'${-- a set param doesn\'t expand to this optional text }'"}'
+ printf '%s\n' '${-##*"hxB"}'
${-##*"hxB"}

Veja? Então é apenas expandido duas vezes. Assim que o shell achar que o parâmetro está configurado, tudo no campo de expansão opcional é descartado, praticamente, e se expande para todo o seu valor, que é removido de si mesmo e, portanto, para nada. Na maioria dos shells, você não precisa nem mesmo passar aspas, mas bash exige isso.

Melhor ainda é:

COMMENT=
echo      ${COMMENT-"
           this will work the same way
           but the stripping isn\'t
           necessary because, while
           the variable is set, it is also
           empty, and so it will expand to
           its value - which is nothing
           "} this you\'ll see
this you'll see
    
por 04.01.2016 / 03:01

Tags