Como passar a saída de um script para um comando como ls sem que a saída seja dividida?

2

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.

    
por helpermethod 28.02.2013 / 15:02

2 respostas

3

Use aspas:

$ ls "$(./myscript)"
    
por 28.02.2013 / 15:14
2

Substituição de comandos

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"

Passando vários nomes de arquivos para um comando

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.

Mais notas sobre substituições desprotegidas e citações

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
por 28.02.2013 / 20:45