Use aspas:
$ ls "$(./myscript)"
Suponha que eu tenha um script como este:
#!/bin/bash
printf '%q\n' "b c"
A execução do script é impressa:
b\ c
na linha de comando.
Agora, estando em um diretório que contém um arquivo chamado b c
, quero passar a saída do meu script para um comando como ls
:
$ ls $(./myscript)
O problema aqui é que b c
é dividido em b\
e c
, ou seja, dois argumentos e ls
obviamente não podem encontrá-los. Existe alguma maneira de contornar isso? Eu pensei que escapar do espaço na saída seria o suficiente.
Se você precisar passar a saída de um comando como argumento para outro comando, use a substituição de comando . Semelhantemente ao que acontece com substituições de variáveis, o resultado da substituição de comando é submetido à divisão de palavras (divisão em palavras separadas em cada caractere em $IFS
, espaço em branco por padrão) e expansão de caractere curinga (globbing de nomes de arquivos). Portanto, sempre use aspas duplas em torno das substituições de comandos , a menos que você queira que a divisão e globbing ocorram.
ls -- "$(./myscript)"
--
informa ls
para parar as opções de processamento, isso é necessário caso o nome do arquivo impresso pelo script comece com -
.
Você tem que imprimir os nomes dos arquivos literais. Não adicione citações extras no script.
Na verdade, há um caso de borda em que o argumento é desconfigurado: a substituição de comando retira qualquer nova linha final da saída do comando. Portanto, se a saída for foo↲bar
ou foo↲bar↲
ou foo↲bar↲↲↲
(eu uso ↲
para representar um caractere de nova linha), o argumento do comando ls
será apenas foo↲bar
. Isso quase nunca acontece na prática, porque embora novas linhas sejam permitidas em nomes de arquivos, ninguém as utiliza. No entanto, pode ser uma preocupação de segurança, porque alguém tentando atacar seu sistema pode colocar novas linhas em um nome de arquivo.
Para lidar com as novas linhas finais, organize o comando para gerar um caractere extra de não-nova linha e retire-o do resultado da substituição de comando.
output=$(./myscript; echo a)
output="${output%a}"
ls -- "$output"
O único caractere que não pode aparecer em um nome de arquivo ou em um argumento de linha de comando é o byte nulo. A maneira segura de gerar vários nomes de arquivos ou outros argumentos a serem passados para um comando é separá-los com bytes nulos e chamar xargs -0
. Isso funciona no Linux, * BSD e OSX, mas é uma extensão do POSIX que outras variantes unix podem não ter. Se a linha de comando resultante for muito longa, o xargs a divide e invoca o comando várias vezes.
… | xargs -0 ls
Sem -0
, xargs
espera um formato de entrada que nenhum outro comando comum produz. É seguro para uso se a entrada não contiver nenhum espaço em branco nem nenhum de \"'
.
Uma maneira de citar a entrada para xargs
é colocar uma barra invertida antes pelo menos dos seguintes caracteres: nova linha, tabulação, espaço e \"'
.
for x in …; do
printf '%s\n' "$x" | sed -e 's/[ '\''\"]/\&/g' -e 's/$/\$/' -e '$ s/\$//'
done | xargs ls
Coloque um espaço e uma tabulação em vez dos dois caracteres de espaço entre parênteses no comando sed.
Em uma variável desprotegida ou em uma substituição de comando, para cada palavra à qual ocorre a globalização, os quatro caracteres \[*?
são expandidos. No entanto, apenas os caracteres [*?
disparam globbing: uma barra invertida sem outros caracteres globbing permanece intacta. Portanto, se o diretório atual contiver três arquivos chamados foo
, goo
e bar
, então:
ls $(echo '?oo bar')
chama ls
com os argumentos foo
, goo
e bar
ls $(echo '?\oo b\ar')
chama ls
com os argumentos foo
, goo
e b\ar