A opção nullglob
(que BTW é uma invenção zsh
, adicionada anos depois a bash
( 2.0
)) não seria ideal em vários casos. E ls
é um bom exemplo:
ls *.txt
Ou o seu equivalente mais correto:
ls -- *.txt
Com nullglob
on, executar ls
sem nenhum argumento tratado como ls -- .
(listar 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 arquivo txt
.
Um padrão mais sensato, e o de csh, tcsh, zsh ou fish 2.3+ (e de shells iniciais do Unix) é cancelar o comando se o glob não corresponder.
bash
(desde a versão 3) tem uma opção failglob
para isso (interessante para esta discussão, pois ao contrário de ash
, AT & T ksh
ou zsh
, bash
não suporta Escopos locais para opções (embora isso mude em 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 de zsh
, fish
ou bash -O failglob
em casos como:
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 torna:
ls -- file.txt
Você pode obter esse comportamento com zsh
com setopt cshnullglob
, embora uma maneira mais sensata de fazer isso em zsh
seria usar um glob como:
ls -- *.(txt|html)
Em zsh
, você também pode aplicar nullglob em uma base por glob, que é uma abordagem muito melhor do que modificar uma configuração global:
files=(*.txt(N))
criaria uma matriz vazia se não houver nenhum arquivo txt
em vez de falhar o comando com um erro (ou torná-lo uma matriz com um argumento *.txt
literal com outros shells).
As versões de fish
anteriores a 2.3 funcionariam como bash -O nullglob
, mas emitiriam um aviso quando interativas quando uma glob não tivesse correspondência. Desde o 2.3, ele funciona como zsh
, exceto para globs usados em for
, set
ou count
.
Agora, na nota de histórico, o comportamento foi realmente quebrado pela shell Bourne. Nas versões anteriores do Unix, o globbing era feito através do /etc/glob
helper e esse ajudante se comportava como csh
: ele falharia o comando se nenhum dos globs correspondesse a nenhum arquivo e removeria os globs sem nenhuma correspondência.
Então, a situação em que estamos hoje é devido a uma má decisão tomada no shell Bourne.
Observe que o shell Bourne (e o shell C) veio com outro novo recurso Unix: o ambiente. Isso significava expansão variável (seu antecessor tinha apenas os parâmetros $1
, $2
... posicionais). O shell Bourne também introduziu a substituição de comandos.
Outra falha na decisão de design do shell Bourne era realizar globbing (e divisão) na expansão de variáveis e na substituição de comando (possivelmente para compatibilidade com o shell Thompson, em que echo $1
ainda invocaria /etc/glob
if $1
continham curingas (era mais parecido com uma expansão de macro pré-processador, como no valor expandido foi analisado novamente como código de shell)).
Globs com falha que não correspondem significaria, por exemplo, que:
pattern='a.*b'
grep $pattern file
falharia o comando (a menos que haja alguns arquivos a.whateverb
no diretório atual). csh
(que também executa globbing na expansão de variáveis) falha o comando nesse caso (e eu diria que é melhor do que deixar um erro inativo lá, mesmo que não seja tão bom quanto não fazer globbing como em zsh
).