Ambos estão errados com as configurações da opção zsh default. Você pode ver facilmente o que está acontecendo usando echo como o comando em vez de mv .
Interativamente, parece que você tem a opção null_glob definida. De acordo com a documentação zsh , essa opção não é configurada por padrão. O que acontece com essa opção unset depende se outra opção, nomatch , está definida ou não definida. Com nomatch unset ( nonomatch ) você obteria isso:
% mkdir dir
% touch dir/file.a
% ls file.a
ls: cannot access file.a: No such file or directory
% echo */*.{a,b} .
dir/file.a */*.b .
A expansão acontece em 2 etapas. Primeiro, */*.{a,b} é expandido para 2 palavras: */*.a e */*.b . Então cada palavra é expandida como um padrão glob. O primeiro se expande para dir/file.a e o segundo se expande para si mesmo porque não corresponde a nada. Tudo isso significa que, se você usar mv e não echo , mv deverá tentar mover 2 arquivos: dir/file.a (fino) e */*.b (nenhum arquivo). Isso é o que acontece por padrão na maioria dos shells, como sh e ksg e bash .
As configurações da opção zsh defaults são de que null_glob não está definido e nomatch está definido. Os scripts são executados com as configurações de opção padrão (a menos que você as altere em ~/.zshenv ou /etc/zshenv , o que você não deveria). Isso significa que, em scripts, você consegue isso:
% mkdir dir
% touch dir/file.a
% ls file.a
ls: cannot access file.a: No such file or directory
% cat script.sh
#!/usr/bin/zsh
echo */*.{a,b} .
% ./script.sh
./script.sh:2: no matches found: */*.b
Como */*.b não corresponde a nada, você recebe um erro devido a nomatch .
Se você inserir setopt nonomatch no script antes do comando echo / mv , retornará ao comportamento incorreto como descrito acima: ele tenta mover um arquivo que não existe.
Se você inserir setopt null_glob no script antes do comando echo / mv , obterá o comportamento obtido em seu shell interativo, que é o que funciona.