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

29

Considerar

echo \ # this is a comment
foo

Isto 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 irmão 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:

Costumava haver um gotocomando shell que ramifica para rótulos especificados como :aqui. O gotose foi, mas você ainda pode usar a : whateversintaxe ... :é uma espécie de comentário analisado agora.

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

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

Consulte também a pergunta anterior Como comentar comandos de várias linhas em scripts de shell? .


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

Uma escolha melhor é a seguinte:

echo `# this is a comment` \
foo
Faheem Mitha
fonte

Respostas:

25

Os comentários terminam na primeira nova linha (consulte a regra 10 de reconhecimento de token do shell ), sem permitir linhas de continuação ; portanto, esse código possui foouma 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, esta é efetivamente uma maneira altamente confusa de inserir um comentário no meio de uma linha.

Não há mágica: :é um comando comum, o utilitário de dois pontos , que não faz nada. O utilitário de dois pontos é útil principalmente quando a sintaxe do shell requer um comando, mas você não tem nada a 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 ainda não estiver definida.

: "${foo:=default value}"

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

Gilles 'SO- parar de ser mau'
fonte
OK eu vejo. Há alguma maneira melhor de inserir comentários nesse contexto que você conhece?
Faheem Mitha
3
@FaheemMitha Conforme recomendado no tópico que você cita: divida seu comando em partes gerenciáveis ​​e comente cada parte. Se o seu comando é tão complicado que precisa de um comentário no meio, é hora de simplificá-lo!
Gilles 'SO- stop be evil'
1
Bem, o comando em questão tem muitos argumentos ... Ele está convertendo vários arquivos de vídeo em um arquivo. Não vejo uma maneira direta de simplificá-lo. Talvez crie uma lista de algum tipo e a passe como argumento? Eu acho que isso poderia ser outra pergunta.
Faheem Mitha
@FaheemMitha Example:, make_FINDum script quickie que cria uma longa lista de argumentos para find. Aqui, a motivação para construí-lo parte por parte é que cada parte vem do corpo de um loop, mas o mesmo estilo permite comentar cada parte.
Gilles 'SO- stop be evil'
1
Obrigado pelo exemplo. Meu exemplo é basicamente apenas uma longa lista de nomes dados como argumentos para um comando. Quero comentários anexados a cada nome, porque essa é a maneira mais fácil de manter o contexto. Não vejo nenhuma maneira óbvia de dividir o comando em pedaços, e se o fizesse, poderia torná-lo ainda mais longo e já é longo o suficiente.
Faheem Mitha
6

Você pode conseguir isso usando matrizes Bash, por exemplo,

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

"${CMD[@]}"

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

O texto entre (e )define a matriz e está sujeito à sintaxe usual do bash; portanto, tudo em uma linha depois #é ignorado.

Nota sobre como preservar o espaço em branco entre aspas

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

Por outro lado, se a expansão estiver entre aspas duplas, ou seja "${CMD[@]}", cada elemento na matriz será preservado. Considere a diferença entre hello world second iteme "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
RobM
fonte
Obrigado pela resposta, @RobM. Então, sua sugestão é incluir os comentários / meta-informações sobre os itens na lista no próprio array bash? Essa pergunta provavelmente seria mais útil se eu tivesse incluído um exemplo do tipo de comando que estava tentando usar. Não sei por que não sabia.
Faheem Mitha
Bah, acontece que eu não li sua pergunta com cuidado. Pensei que você estava fazendo a pergunta à qual estava vinculado. Opa :)
RobM
${CMD[@]}não se expande para uma única sequência. - Ele se expande para várias strings: não apenas é dividido para cada elemento da matriz, mas também no espaço em branco em cada elemento. (Ie: completamente inútil)
Robert Siemer
4

Não faça $(: comment). Isso não é um comentário - é um subshell - outro processo de shell totalmente novo para a maioria dos shells. Seu objetivo é fazer menos com sua contribuição, não mais, 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 lá é que o shell está fazendo uma substituição. Ele substitui o valor do parâmetro shell especial $-duas vezes a cada vez. De qualquer maneira, é uma cadeia curta, mas sempre é definida - e, portanto, a substituição interna - que é interpretada como um padrão a ser retirado da parte externa - não se 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"}

Vejo? Então, ele é expandido apenas duas vezes. Assim que o shell descobre que o parâmetro está definido, tudo no campo de expansão opcional é descartado, praticamente e se expande para todo o valor que é removido de si mesmo e, portanto, para nada. Na maioria das conchas, você nem precisa escapar de aspas, mas bashexige.

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
mikeserv
fonte