Em zsh
rm foo.bar
imprimerm: foo.bar: No such file or directory
.rm foo.bar 2>/dev/null
imprime nada, como eu esperava.
Mas se o comando contiver correspondências de padrão, o erro não será suprimido por 2>/dev/null
:
rm *.bar
imprimezsh: no matches found: *.bar
.rm *.bar 2>/dev/null
imprime o mesmo.
Existe uma maneira geral de suprimir mensagens de erro no zsh? Um método simples para todos os tipos de mensagens de erro.
Quando você tenta executar
rm *.bar
e não há nenhum arquivo que corresponda a*.bar
, basicamente duas coisas podem acontecer:O comportamento POSIX é que o shell é executado
rm
com literal*.bar
como argumento. A ferramenta tenta remover um arquivo literalmente chamado*.bar
(como se você executasserm '*.bar'
), ele falha e imprime algo comorm: *.bar: No such file or directory
. É assim que o shell POSIX (sh
) e os shells compatíveis se comportam.O comportamento não POSIX é o shell detecta que não há correspondência, imprime algo como e
no matches found: *.bar
não é executadorm
. É assim que o Zsh se comporta por padrão. (Para comparação: no Bash, você pode alternar para esse comportamento porshopt -s failglob
.)Um erro de
rm
(o caso anterior) é impresso no stderr derm
. Um erro do shell (o último caso) é impresso no stderr do shell.O redirecionamento em
rm *.bar 2>/dev/null
afeta o stderr derm
apenas + . No seu exemplorm
nem roda.Para redirecionar o stderr do shell principal, você precisa do
exec
. Por "o shell principal" quero dizer o shell interativo onde você digita; ou, no caso de execução de um script, o shell interpretando o script. Exemplo:No Zsh você pode até fechá-lo:
Uma abordagem razoável é duplicar o stderr original antes, caso você precise usá-lo (ou restaurá-lo) mais tarde. Exemplo (observe se você deseja colar este código em um Zsh interativo, então você deve invocar
setopt interactive_comments
primeiro):Aqui
7
está um número arbitrário de um dígito, caso contrário, não utilizado como descritor de arquivo. As duas primeiras linhas podem ser escritas como uma:exec 7>&2 2>/dev/null
; da mesma forma, as duas últimas linhas podem.Note
rm
ou qualquer comando (comowhatever
) invocado sem redirecionamento stderr herda stderr do shell. Isso significa que no exemplo acimarm
(se for executado) ewhatever
imprimirá suas mensagens de erro (se houver) para/dev/null
. Depoisexec 2>/dev/null
você pode invocar qualquer número ou comandos e todos eles terão/dev/null
como stderr. Se você realmente deseja suprimir todos os tipos de mensagens de erro, esse é o caminho.A solução funciona em muitos shells, não apenas no Zsh. Se você deseja redirecionar o stderr de um shell interativo, lembre-se de que alguns shells menos sofisticados usam o stderr para imprimir seu prompt.
Observe que um processo pode redirecionar seu próprio stderr (como nosso shell faz com
exec 2>…
); ou pode imprimir mensagens de erro para stdout ou para/dev/tty
(não deveria, mas tecnicamente pode). Portantoexec 2>/dev/null
, não garante que você não verá mensagens que se pareçam com mensagens de erro.Depois
exec 2>/dev/null
você ainda pode redirecionar o stderr de qualquer comando sob demanda. Por exemplo, se em vez dissowhatever
tivéssemos:então suas mensagens de erro iriam para o stderr original que deliberadamente salvamos como descritor de arquivo
7
.exec
não é a única maneira de redirecionar stderr (ou outro descritor de arquivo) de um shell. Você pode tratar um shell (mas não todo o shell principal) como você tratarm
emrm … 2>/dev/null
. Você pode tratar um subshell assim:ou apenas algum código interpretado pelo shell principal:
(
;
aqui é opcional no Zsh, obrigatório em alguns outros shells; eu só queria que o código funcionasse fora do Zsh também).Qualquer uma dessas linhas pode resolver seu problema. No contexto de suprimir mensagens de erro provenientes de um shell, as duas linhas são equivalentes ++ .
Observe que o redirecionamento começa em
(
/{
e termina em)
/}
, seu escopo é limitado. Ainda assim, você pode colocar muitos comandos dentro dos parênteses, então o escopo "limitado" pode ser o script inteiro. Ou pode ser apenas uma parte do script (incluindowhatever
apresentado anteriormente nesta resposta, o que você quiser). O fato de o redirecionamento parar de funcionar após)
/}
torna essa abordagem uma alternativa interessante para salvar e restaurar stderr comexec
.+ Estritamente: afeta "
rm
" antes e depois de se tornar (ou tentar se tornar)rm
. Tenha paciencia comigo. O redirecionamento emrm … 2>/dev/null
começa como um redirecionamento executado por um shell bifurcado que está prestes a se substituir porrm
executável . Este shell redireciona seu próprio stderr antes de tentar se substituir porrm
. Qualquer erro reportado após realizar o redirecionamento irá para/dev/null
. Por exemplo, serm
não for encontrado, o redirecionamento já executado suprimirá ocommand not found
erro.++ Tecnicamente
( … )
cria um subshell,{ … }
não. Um subshell se comporta como um processo de shell separado que herda variáveis e diretório de trabalho atual, mas não pode alterar nada (como variáveis ou diretório de trabalho atual) em seu shell pai. Pode ou não ser realizado como um processo verdadeiramente separado, é o comportamento que importa. O OTOH{ … }
apenas agrupa comandos para algum propósito (no nosso caso o propósito é redirecionamento). Isso não significa que nunca há um subshell quando você usa{ … }
; por exemplo, em um pipeline, todas as partes, exceto a última, são subshells (em alguns shells: todas as partes, incluindo a última). Esses detalhes técnicos são irrelevantes apenas no contexto do nosso problema, mas em geral podem fazer a diferença.