Eu estava tentando escrever um script curto que gravaria todos os programas executáveis encontrados em $PATH
:
for dir in $(tr ':' ' ' <<<"${PATH}"); do
for pgm in $dir/*; do
if command -v "${pgm}" >/dev/null 2>&1; then
echo "${pgm}"
fi
done
done | sort >file
No bash, ele funciona como esperado, mas o zsh pára de processar o script assim que a geração do nome do arquivo falha no loop interno:
for pgm in $dir/*; do
^^^^^^
...
done
Como resultado, como meu $PATH
contém um diretório que não contém nenhum arquivo ( /usr/local/sbin
), em zsh, o script falha ao gravar os executáveis encontrados nos diretórios posteriormente.
Este é outro código que mostra o mesmo problema:
for f in /not_a_dir/*; do
echo 'in the loop'
done
echo 'after the loop'
No bash, este comando gera:
in the loop
after the loop
E sai com o código 0
.
Enquanto em zsh, o mesmo comando gera:
no matches found: /not_a_dir/*
E sai com o código 1
.
A diferença de comportamento entre os shells parece vir da opção nomatch
, que é descrita em man zshoptions
:
NOMATCH (+3) <C> <Z>
If a pattern for filename generation has no matches, print an error, instead of leaving it unchanged in the argument list. This also applies to file expansion of an initial
~
or=
.
E também explicado em man zshexpn
(section FILENAME GENERATION):
The word is replaced with a list of sorted filenames that match the pattern. If no matching pattern is found, the shell gives an error message, unless the NULL_GLOB option is set, in which case the word is deleted; or unless the NOMATCH option is unset, in which case the word is left unchanged.
Porque se eu cancelar a configuração de nomatch
, o zsh se comportará como o bash:
unsetopt nomatch
for f in /not_a_dir/*; do
echo 'in the loop'
done
echo 'after the loop'
Agora eu entendo a diferença de comportamentos entre o bash e o zsh, e porque o script gera um erro no zsh, mas eu quero entender por que uma geração de nome de arquivo falhada faz com que o zsh pare imediatamente de processar um script.
Então, tentei reproduzir o mesmo problema substituindo a geração de nome de arquivo com falha por um comando com falha (executando not_a_cmd
):
for f in ~/*; do
not_a_cmd
done
echo 'after the loop'
Mas a saída deste script é quase idêntica em ambos os shells (além das mensagens de erro devidas a not_a_cmd
). Em particular, ambas as conchas são impressas:
after the loop
E ambos os shells saem com o código 0
.
Por que uma geração de nome de arquivo com falha (como for f in /not_a_dir/*
) faz com que o zsh pare de processar um script, mas não um comando com falha (como not_a_cmd
)?
Estou usando zsh 5.6.2-dev-0 (x86_64-pc-linux-gnu)
.
Tags zsh