Já é hora de resolver esse enigma que me incomoda há anos...
Eu tenho encontrado isso de vez em quando e pensei que este é o caminho a seguir:
$(comm "$(arg)")
E pensei que minha visão era fortemente apoiada pela experiência. Mas 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 Shellcheck 0.4.4, btw.)
Você precisa usar
"$(somecmd "$file")"
.Sem as aspas, um caminho com um espaço será dividido no argumento para
somecmd
, e terá como destino 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, então você precisa de aspas do lado de fora de toda a substituição do comando.As aspas dentro da substituição de comando não têm efeito sobre as aspas fora dela. O próprio manual de referência do Bash não é muito claro sobre isso, mas o BashGuide o menciona explicitamente . O texto em POSIX também exige isso, pois "qualquer script de shell válido" é permitido dentro de
$(...)
:Exemplo:
uma. Sem cotações,
dirname
processa ambos./space
ehere/foo
:b. Citações dentro,
dirname
processos./space here/foo
, dando./space here
, que é dividido em dois:c. Aspas externas,
dirname
processam ambos./space
ehere/foo
, saídas em linhas separadas, mas agora as duas linhas formam um único argumento :d. Citações dentro e fora, isso dá a resposta correta:
(possivelmente teria sido mais simples se
dirname
apenas processasse o primeiro argumento, mas isso não mostraria a diferença entre os casos a e c.)Observe que com
dirname
(e possivelmente outros) você também precisa adicionar--
, para evitar que o nome do arquivo seja considerado uma opção caso comece com um traço, então use"$(dirname -- "$file")"
.