A nullglob
opção (que BTW é uma zsh
invenção, adicionada apenas anos depois a bash
( 2.0
)) não seria ideal em vários casos. E ls
é um bom exemplo:
ls *.txt
Ou seu equivalente mais correto:
ls -- *.txt
Com nullglob
on seria executado ls
sem argumento que é tratado como ls -- .
(liste o diretório atual) se nenhum arquivo corresponder, o que provavelmente é pior do que chamar ls
com um literal *.txt
como argumento.
Você teria problemas semelhantes com a maioria dos utilitários de texto:
grep foo *.txt
Procuraria foo
no stdin se não houver txt
arquivo.
Um padrão mais sensato, e o de csh, tcsh, zsh ou fish 2.3+ (e dos shells iniciais do Unix) é cancelar o comando completamente se o glob não corresponder.
bash
(desde a versão 3) tem uma failglob
opção para isso (interessante para esta discussão, pois ao contrário da ash
AT&T ksh
ou zsh
, bash
não suporta escopos locais para opções (embora isso mude no 4.4), essa opção, quando ativada globalmente, quebra algumas coisas como as funções de conclusão do bash).
Observe que csh e tcsh são um pouco diferentes ou zsh
, em casos como:fish
bash -O failglob
ls -- *.txt *.html
Onde você precisa que todos os globs não correspondam para que o comando seja cancelado. Por exemplo, se houver um arquivo txt e nenhum arquivo html, isso se tornará:
ls -- file.txt
Você pode obter esse comportamento com zsh
com setopt cshnullglob
embora uma forma mais sensata de fazê-lo em zsh
seria a utilização de um glob como:
ls -- *.(txt|html)
No zsh
e ksh93
, você também pode aplicar o nullglob por glob, o que é uma abordagem muito mais saudável do que modificar uma configuração global:
files=(*.txt(N)) # zsh
files=(~(N)*.txt) # ksh93
criaria uma matriz vazia se não houver txt
arquivo em vez de falhar no comando com um erro (ou torná-la uma matriz com um *.txt
argumento literal com outras conchas).
As versões fish
anteriores à 2.3 funcionariam como bash -O nullglob
um aviso quando interativas quando uma glob não tem correspondência. Desde a versão 2.3, funciona como, zsh
exceto os globs usados em for
, set
ou count
.
Agora, na nota da história, o comportamento foi realmente quebrado pelo shell Bourne. Nas versões anteriores do Unix, o globbing era feito através do /etc/glob
auxiliar e ele se comportava da seguinte maneira csh
: falharia no comando se nenhum dos globs correspondesse a qualquer arquivo e os removesse sem nenhuma correspondência.
Portanto, a situação em que estamos hoje se deve a uma má decisão tomada no shell Bourne.
Observe que o shell Bourne (e o shell C) veio com outro novo recurso do Unix: o ambiente. Essa expansão variável significava (é predecessor só tinha os $1
, $2
... parâmetros posicionais). O shell Bourne também introduziu a substituição de comandos.
Outra má decisão de design do shell Bourne foi realizar globbing (e divisão) após a expansão de variáveis e substituição de comandos (possivelmente para compatibilidade com versões anteriores com o shell Thompson, onde echo $1
ainda invocaria /etc/glob
se houvesse $1
curingas (era mais como expansão macro de pré-processador) lá, como no valor expandido, foi analisado novamente como código shell)).
Globs com falha que não correspondem significam, por exemplo, que:
pattern='a.*b'
grep $pattern file
falharia no comando (a menos que haja alguns a.whateverb
arquivos no diretório atual). csh
(que também executa globbing na expansão de variáveis) falha no comando nesse caso (e eu diria que é melhor do que deixar um bug inativo lá, mesmo que não seja tão bom quanto não fazer globbing como em zsh
).