arquivos correspondentes usando expansão de chaves em zsh

5

Em bash , posso usar chaves para combinar com vários arquivos, por exemplo,

ls *.{dot,svg,err}

Se não houver arquivo para uma extensão específica, recebo um aviso, mas os arquivos restantes serão listados.

ls: cannot access '*.err': No such file or directory

No entanto , em zsh , recebo um erro e nenhuma saída é produzida para os arquivos existentes.

zsh: no matches found: *.err

Isto também se aplica à remoção, quando tentar remover arquivos com isto:

rm -f *.{dot,svg,err}

Removerá todos os arquivos correspondentes em bash sem nenhum erro ou aviso. mas em zsh ele vai errar e não remover nada.

Existe uma maneira de fazer com que zsh se comporte da mesma forma que bash , ou existe outra maneira de remover / listar os arquivos existentes com facilidade semelhante?

¹Obviamente, posso fazer algo como find . -name '*.dot' -or -name '*.svg' -or -name '.err' | xargs rm , mas isso não é particularmente fácil / prático.

    
por Peter Uhnak 27.07.2016 / 07:49

2 respostas

9

Você não recebe um aviso em bash , você recebe um erro por ls (você verá que ls status de saída é não -0, que indica que falhou).

Em zsh e bash , {...} não é um operador de globbing, é uma expansão que ocorre antes da globulação.

Em:

ls -d -- *.{dot,svg,err}

(você esqueceu o -d e o -- btw), o shell expande o {...} primeiro:

ls -d -- *.dot *.svg *.err

e depois faz o glob. bash , como a maioria das shells parecidas com Bourne, tem essa característica errada de que globs não correspondentes são passados como estão. Enquanto em zsh , um glob não correspondente é um erro.

Veja como rm -f [ab].c in bash poderia excluir o arquivo [ab].c se não houvesse nenhum arquivo chamado a.c nor b.c . Em zsh , você receberia um erro no match . Veja a opção failglob em bash para obter um comportamento semelhante.

ls -d -- *.{dot,svg,err}(N)

em zsh habilitaria a opção nullglob em todos os 3 globs, de modo que, se os globs não corresponderem, eles serão removidos, mas provavelmente não é o que você deseja, porque nenhum dos globs corresponde a nenhum arquivo , o comando se tornará:

ls -d --

Que listará . (o diretório atual).

Melhor é usar um glob que corresponda aos arquivos com uma das três extensões:

ls -d -- *.(dot|svg|err)

Isso fornecerá uma lista classificada de arquivos para ls , ls será executada, a menos que não haja nenhum arquivo que corresponda a esse padrão.

Você também tem a opção de ativar o comportamento sh / bash (bogus IMO) com emulate sh ou com unsetopt nomatch . Uma abordagem um pouco melhor é ativar o comportamento csh (que também era o comportamento de shells Unix antes da liberação do shell Bourne):

setopt cshnullglob

Com essa opção, o comando é cancelado apenas se all as globs na linha de comando não corresponderem. Se pelo menos uma corresponder, todas as que não corresponderem serão removidas, então:

ls -d -- *.{dot,svg,err}

Expandirá os dot , svg e err , omitindo os que faltam.

Se você quiser comparar o efeito na ordem dos argumentos das diferentes abordagens, você precisa de um comando que (ao contrário de ls ) não os ordene antes de exibir. Com GNU ls , você pode passar a opção -U para isso, ou desde que ls imprima seus argumentos aqui, use apenas printf '%s\n' *... .

    
por 27.07.2016 / 14:58
3

Ligue (N) ao padrão; isso diz ao zsh para não ficar chateado com a falta de correspondência.

rm -f *.{dot,svg,err}(N)

É um recurso zsh glob. Veja man zshexpn para mais detalhes; em "Qualificadores Globais". É anotado como "define a opção NULL_GLOB para o padrão atual".

    
por 27.07.2016 / 09:32