Qual é o caminho certo para citar $ (comando $ arg)?

15

Já é hora de resolver este enigma que está me incomodando há anos ...

Eu tenho encontrado isso de vez em quando e achei que esse é o caminho a seguir:

$(comm "$(arg)")

E pensei que minha visão era strongmente apoiada pela experiência. Mas eu não tenho mais tanta certeza. Shellcheck também não consegue se decidir. É ambos:

"$(dirname $0)"/stop.bash
           ^-- SC2086: Double quote to prevent globbing and word splitting.

E:

$(dirname "$0")/stop.bash
^-- SC2046: Quote this to prevent word splitting.

Qual é a lógica por trás?

(é a versão 0.4.4 do Shellcheck).

    
por Tomasz 15.05.2018 / 20:19

1 resposta

43

Você precisa usar "$(somecmd "$file")" .

Sem as aspas, um caminho com um espaço será dividido no argumento para somecmd e ele será direcionado para o arquivo errado. Então você precisa de citações no interior.

Quaisquer espaços na saída de somecmd também causarão divisão, portanto, você precisará de cotações do lado de fora de toda a substituição de comandos.

As citações dentro da substituição do comando não têm efeito sobre as citações fora dele. O manual de referência do próprio Bash não é muito claro sobre isso, mas O BashGuide menciona isso explicitamente . O texto em POSIX também exige, como "qualquer script de shell válido" é permitido dentro de $(...) :

With the $(command) form, all characters following the open parenthesis to the matching closing parenthesis constitute the command. Any valid shell script can be used for command, except a script consisting solely of redirections which produces unspecified results.

Exemplo:

$ file="./space here/foo"

a. Sem cotações, dirname processa os dois ./space e here/foo :

$ printf "<%s>\n" $(dirname $file)
<.>
<here>

b. Citações dentro, dirname processa ./space here/foo , dando ./space here , que é dividido em dois:

$ printf "<%s>\n" $(dirname "$file")
<./space>
<here>

c. Citações fora, dirname processa ./space e here/foo , saídas em linhas separadas, mas agora as duas linhas formam um único argumento :

$ printf "<%s>\n" "$(dirname $file)"
<.
here>

d. Citações dentro e fora, isso dá a resposta correta:

$ printf "<%s>\n" "$(dirname "$file")"
<./space here>

(que possivelmente seria mais simples se dirname processasse apenas o primeiro argumento, mas isso não mostrasse a diferença entre os casos a e c.)

Observe que, com dirname (e possivelmente outros), você também precisa adicionar -- para impedir que o nome do arquivo seja usado como opção, caso comece com um traço, use "$(dirname -- "$file")" .

    
por 15.05.2018 / 20:22