Eu tenho o seguinte script bash simplificado
#!/bin/bash
files=("$@")
if [ "X$files" = "X" ]; then
files=$HOME/print/*.pdf;
fi
for file in "${files[@]}"; do
ls "$file";
done
Se eu passar argumentos (nomes de arquivos) como parâmetros, esse script imprimirá os nomes de arquivos adequados. Por outro lado, se eu não passar argumentos, ele imprimirá
/home/user/print/*.pdf: No such file or directory
Por que os nomes de arquivo não são expandidos nesse caso e como corrigi-lo? Observe que eu uso as construções files=("$@")
e "${files[@]}"
porque li que é preferível aos "arquivos = $ *" usuais.
files=$*
sempre usual ? Isso está errado .Respostas:
Você está atribuindo
files
como uma variável escalar em vez de uma variável de matriz .No
Você está atribuindo uma string como
/home/highsciguy/print/*.pdf
à$files
variável escalar (também conhecida como string).Usar:
ou
em vez de. O shell expandirá esse padrão globbing em uma lista de caminhos de arquivo e atribuirá cada um deles aos elementos da
$files
matriz .A expansão da glob é feita no momento da atribuição.
Você não precisa usar recursos sh não-padrão e pode usar o sistema em
sh
vez debash
aqui escrevendo-o:set
é atribuir a"$@"
matriz de parâmetros posicionais.Outra abordagem poderia ter sido armazenar o padrão de globbing em uma variável escalar:
E faça com que a concha expanda a glob no momento em que a
$files
variável for expandida.Aqui, como
$files
não é citado (o que você normalmente não deve fazer), sua expansão está sujeita à divisão de palavras (que desativamos aqui) e geração de globbing / nome de arquivo.Portanto, o
*.pdf
será expandido para a lista de arquivos correspondentes. No entanto, se$HOME
contiverem caracteres curinga, eles também poderão ser expandidos, motivo pelo qual ainda é preferível usar uma variável de matriz.fonte
Você pode ter visto coisas como
files=$*
efiles=~/print/*.pdf
em conchas mais antigas, sem matrizes, e entãols $files
.Uma substituição de variável que não esteja entre aspas duplas interpreta o valor da variável como uma lista separada por espaços em branco de padrões curinga de shell que são substituídos por nomes de arquivos correspondentes, se houver algum. Por exemplo, depois
files=~/print/*.pdf
,ls $files
expande para algo comols
os argumentos/home/highsciguy/print/bar.pdf
,/home/highsciguy/print/foo.pdf
etc. No casofiles=$*
, essa atribuição concatena os argumentos passados para o script com espaços no meio e osls $files
divide novamente.Tudo isso se quebra se você tiver nomes de arquivos que contenham espaços em branco ou caracteres brilhantes, e é por isso que você não deve fazer as coisas dessa maneira. Use matrizes.
Observe que
var=(…)
."$files"
está vazio quandofiles
é uma matriz cujo elemento do índice 0 está desmarcado ou uma sequência vazia. Também[ "X$foo" = "X" ]
é uma maneira obsoleta de testar se$foo
está vazio: todas as conchas modernas são implementadas[ -n "$foo" ]
corretamente. No bash, você pode usar[[ -n $foo ]]
.Em shells que não suportam matrizes, existe de fato uma matriz: os parâmetros posicionais para a função shell ou atual. Aqui, você realmente não precisa da
files
matriz, na verdade, seria mais fácil usar os parâmetros posicionais.fonte