com bash
:
shopt -s nullglob
files=(/mydir/*.gz)
((${#files[@]} == 0)) || gzip -d -- "${files[@]}"
com zsh
:
files=(/mydir/*.gz(N))
(($#files == 0)) || gzip -d -- $files
Observe que em zsh
, sem (N)
, como em shells pré-Bourne, csh ou tcsh, se o glob não corresponder, o comando não é executado, você só faria o acima para evitar mensagem de erro resultante (de nenhuma correspondência encontrada em oposição a gzip
com falha no glob expandido no caso de bash
ou outros shells semelhantes a Bourne). Você pode obter o mesmo resultado com bash
com shopt -s failglob
.
Em zsh
, uma falha na glob é um erro fatal que faz com que o shell (quando não interativo) saia. Você pode impedir que o script seja encerrado nesse caso usando um subshell ou usando o mecanismo de captura de erros zsh
( { try-block; } always { error-catching; }
), (ou definindo o nonomatch
(para funcionar como sh
), nullglob
ou noglob
opção é claro, embora eu não recomende isso):
$ zsh -c 'echo zz*; echo not output'
zsh:1: no matches found: zz*
$ zsh -c '(echo zz*); echo output'
zsh:1: no matches found: zz*
output
$ zsh -c '{echo zz*;} always {TRY_BLOCK_ERROR=0;}; echo output'
zsh:1: no matches found: zz*
output
$ zsh -o nonomatch -c 'echo zz*; echo output'
zz*
output
Com ksh93
ksh93
acabou por adicionar um mecanismo semelhante ao qualificador zsh
' (N)
glob para evitar a necessidade de definir uma opção nullglob
globalmente:
files=(~(N)/mydir/*.gz)
((${#files[@]} == 0)) || gzip -d -- "${files[@]}"
POSIXly
Portável em POSIX sh
, onde globs não correspondentes são passados unexpanded sem nenhuma maneira de desabilitar esse comportamento (a única opção relacionada com POSIX glob é noglob
para desativar globbing), o truque é fazer algo como:
set -- /mydir/[*].gz /mydir/*.gz
case $#$1$2 in
'2/mydir/[*].gz/mydir/*.gz') : no match;;
*) shift; gzip -d -- "$@"
esac
A idéia é que, se /mydir/*.gz
não corresponder, ele se expandirá para si mesmo ( /mydir/*.gz
). No entanto, ele também poderia se expandir para que, se houvesse um arquivo realmente chamado /mydir/*.gz
, para diferenciar os casos, também usaríamos o /mydir/[*].gz
glob que também seria expandido para /mydir/*.gz
se houvesse um arquivo chamado assim .
Como isso é muito estranho, você pode preferir usar find
nesses casos:
find /mydir/. ! -name . -prune ! -name '.*' \
-name '*.gz' -type f -exec gzip -d {} +
O ! -name . -prune
é não procurar em subdiretórios (algumas find
implementações têm -depth 1
ou -mindepth 1 -maxdepth 1
como equivalente). ! -name '.*'
é excluir arquivos ocultos, como os globs.
Um benefício é que ainda funciona se a lista de arquivos for grande demais para caber no limite do tamanho dos argumentos para um comando executado ( find
executará vários comandos gzip
se precisar evitar isso, ksh93
e zsh
também têm mecanismos para contornar isso).
Outro benefício é que você receberá mensagens de erro se find
não puder ler o conteúdo de /mydir
ou não puder determinar o tipo dos arquivos (os globs ignorariam silenciosamente o problema e agiriam como se os arquivos correspondentes não existe).
Um pequeno ponto negativo é que você perde o valor exato do status de saída gzip
(se qualquer uma gzip
invocação falhar com um status de saída diferente de zero, find
ainda sairá com um valor diferente de zero status de saída (embora não necessariamente o mesmo), por isso é bom o suficiente para a maioria dos casos de uso).
Outro benefício é que você pode adicionar o -type f
para evitar a descompactação de diretórios ou fifos / devices / sockets ... cujo nome termina em .gz
. Exceto em zsh
( *.gz(.)
apenas para arquivos regulares), globs não pode filtrar por tipos de arquivo, você precisa fazer coisas como:
set --
for f in /mydir/*.gz
[ -f "$f" ] && [ ! -L "$f" ] && set -- "$@" "$f"
done
[ "$#" -eq 0 ] || gzip -d -- "$@"