Digamos que eu tenho um script como o seguinte:
useless.sh
echo "This Is Error" 1>&2
echo "This Is Output"
E eu tenho outro script de shell:
alsoUseless.sh
./useless.sh | sed 's/Output/Useless/'
Eu quero capturar "This Is Error", ou qualquer outro stderr de useless.sh, em uma variável. Vamos chamá-lo de erro.
Observe que estou usando stdout para alguma coisa. Quero continuar usando o stdout, portanto, redirecionar o stderr para o stdout não é útil, neste caso.
Então, basicamente, eu quero fazer
./useless.sh 2> $ERROR | ...
mas isso obviamente não funciona.
Eu também sei que eu poderia fazer
./useless.sh 2> /tmp/Error
ERROR=`cat /tmp/Error`
mas isso é feio e desnecessário.
Infelizmente, se não houver respostas aqui, é isso que terei que fazer.
Espero que exista outro caminho.
Alguém tem alguma ideia melhor?
ERROR=$(./useless.sh | sed 's/Output/Useless/' 2>&1 1>/dev/ttyX)
Respostas:
Seria mais fácil capturar o arquivo de erro assim:
O shell reconhece isso e não precisa executar '
cat
' para obter os dados.A questão maior é difícil. Eu não acho que exista uma maneira fácil de fazer isso. Você precisaria criar todo o pipeline no sub-shell, enviando sua saída padrão final para um arquivo, para que você possa redirecionar os erros para a saída padrão.
Observe que o ponto e vírgula é necessário (em conchas clássicas - Bourne, Korn - com certeza; provavelmente em Bash também). O '
{}
' faz o redirecionamento de E / S sobre os comandos incluídos. Como está escrito, também capturaria errossed
.fonte
/dev/null
vez deoutfile
(Se você é como eu, encontrou essa pergunta pelo Google e não tem os mesmos requisitos do OP)stdout
e parastderr
frente e para trás. Mas cuidado , como é dito aqui : no bash, seria melhor não supor que o descritor de arquivo 3 não seja usado " .alsoUseless.sh
Isso permitirá que você canalizar a saída do seu
useless.sh
roteiro através de um comando comosed
e salvar ostderr
em uma variável chamadaerror
. O resultado do tubo é enviadostdout
para exibição ou a ser canalizado para outro comando.Ele configura alguns descritores de arquivo extras para gerenciar os redirecionamentos necessários para fazer isso.
fonte
stderr
estdout
em variáveis?dry_run
função que pode escolher com segurança entre repetir seus argumentos e executá-los, independentemente de o comando que está sendo executado a seco estiver sendo canalizado para algum outro arquivo.read
não aceita entrada de um tubo. Você pode usar outras técnicas para alcançar o que está tentando demonstrar.Redirecionado stderr para stdout, stdout para / dev / null e, em seguida, use os backticks ou
$()
para capturar o stderr redirecionado:fonte
PY_VERSION="$(python --version 2>&1)"
Há muitas duplicatas para esta pergunta, muitas das quais com um cenário de uso um pouco mais simples, no qual você não deseja capturar stderr e stdout e o código de saída, tudo ao mesmo tempo.
funciona para o cenário comum em que você espera uma saída adequada no caso de êxito ou uma mensagem de diagnóstico no stderr no caso de falha.
Observe que as instruções de controle do shell já examinam
$?
sob o capô; então qualquer coisa que pareçaé apenas uma maneira desajeitada e uniomática de dizer
fonte
fonte
command
é uma má escolha aqui, na medida em que há realmente um builtin com esse nome. Pode torná-loyourCommand
ou algo assim, para ser mais explícito.Para o benefício do leitor, esta receita aqui
Se você quiser pegar
stderr
de algumcommand
emvar
que você pode fazerDepois você tem tudo:
Se
command
é simples (não algo parecidoa | b
), você pode deixar o interior{}
fora:Embrulhado em uma
bash
função reutilizável fácil (provavelmente precisa da versão 3 e posteriorlocal -n
):Explicado:
local -n
aliases "$ 1" (que é a variável paracatch-stderr
)3>&1
usa o descritor de arquivo 3 para salvar pontos stdout{ command; }
(ou "$ @") executa o comando na saída capturando$(..)
2>&1
redirecionastderr
para a saída capturando$(..)
1>&3
redireciona parastdout
fora da captura de saída de$(..)
volta para o "externo"stdout
que foi salvo no descritor de arquivo 3. Observe questderr
ainda se refere ao local onde o FD 1 apontou antes: Para a captura de saída$(..)
3>&-
em seguida, fecha o descritor de arquivo 3, pois ele não é mais necessário, de modo que decommand
repente não aparece nenhum descritor de arquivo aberto desconhecido. Observe que o shell externo ainda tem o FD 3 aberto, mascommand
não o verá.lvm
queixam-se de descritores de arquivos inesperados. Elvm
reclamastderr
- exatamente o que vamos capturar!Você pode capturar qualquer outro descritor de arquivo com esta receita, se você se adaptar adequadamente. Exceto o descritor de arquivo 1, é claro (aqui a lógica de redirecionamento estaria errada, mas para o descritor de arquivo 1 você pode simplesmente usar
var=$(command)
como de costume).Observe que isso sacrifica o descritor de arquivo 3. Se você precisar desse descritor de arquivo, sinta-se à vontade para alterar o número. Mas esteja ciente de que algumas conchas (da década de 1980) podem entender
99>&1
como argumento9
seguido por9>&1
(isso não é problema parabash
).Observe também que não é particularmente fácil tornar esse FD 3 configurável por meio de uma variável. Isso torna as coisas muito ilegíveis:
Notas:
catch-var-from-fd-by-fd var 2 3 cmd..
é o mesmo quecatch-stderr var cmd..
shift || return
é apenas uma maneira de evitar erros feios, caso você esqueça de fornecer o número correto de argumentos. Talvez encerrar o shell seja outra maneira (mas isso dificulta o teste a partir da linha de comando).exec
, mas então fica realmente feia.bash
outras que não sejam necessáriaslocal -n
. No entanto, você não pode usar variáveis locais e isso fica extremamente feio!eval
s são usados de maneira segura. Geralmenteeval
é considerado perigoso. No entanto, neste caso, não é mais mau do que usar"$@"
(para executar comandos arbitrários). No entanto, certifique-se de usar a citação exata e correta, como mostrado aqui (caso contrário, isso se torna muito, muito perigoso ).fonte
Aqui está como eu fiz isso:
Exemplo de uso:
Ele faz usar um arquivo temporário. Mas pelo menos as coisas feias estão envolvidas em uma função.
fonte
eval
. Por exemplo,printf -v "$1" '%s' "$(<tmpFile)"
não corre o risco de executar código arbitrário se suaTMPDIR
variável tiver sido definida como um valor malicioso (ou se o nome da variável de destino contiver esse valor).rm -- "$tmpFile"
é mais robusto querm $tmpFile
.Esse é um problema interessante para o qual eu esperava que houvesse uma solução elegante. Infelizmente, acabo com uma solução semelhante ao Sr. Leffler, mas acrescentarei que você pode chamar de inútil de dentro de uma função Bash para melhorar a legibilidade:
Todo outro tipo de redirecionamento de saída deve ser apoiado por um arquivo temporário.
fonte
POSIX
STDERR pode ser capturado com alguma mágica de redirecionamento:
Observe que a tubulação de STDOUT do comando (aqui
ls
) é feita na parte interna{
}
. Se você estiver executando um comando simples (por exemplo, não um pipe), poderá remover esses aparelhos internos.Você não pode canalizar para fora do comando, pois a tubulação cria um subshell dentro
bash
ezsh
, e a atribuição à variável no subshell não estaria disponível para o shell atual.festança
Em
bash
, seria melhor não supor que o descritor de arquivo 3 não seja usado:Observe que isso não funciona
zsh
.Graças a esta resposta para a idéia geral.
fonte
Este post me ajudou a encontrar uma solução semelhante para meus próprios propósitos:
Então, desde que nossa MESSAGE não seja uma string vazia, passamos para outras coisas. Isso nos informará se nosso format_logs.py falhou com algum tipo de exceção python.
fonte
Capturar e imprimir stderr
Demolir
Você pode usar
$()
para capturar stdout, mas deseja capturar stderr. Então você troca stdout e stderr. Usando fd 3 como armazenamento temporário no algoritmo de troca padrão.Se você deseja capturar E imprimir, use
tee
uma duplicata. Nesse caso, a saída detee
será capturada em$()
vez de ir para o console, mas stderr (oftee
) ainda irá para o console, por isso usamos como segunda saída paratee
o arquivo especial,/dev/fd/2
poistee
espera um caminho de arquivo em vez de um fd número.NOTA: São muitos redirecionamentos em uma única linha e a ordem é importante.
$()
está pegando o stdouttee
no final do pipeline e o próprio pipeline direciona stdout./useless.sh
para o stdin detee
AFTER depois do qual trocamos stdin e stdout./useless.sh
.Usando stdout de ./useless.sh
O OP disse que ainda queria usar (e não apenas imprimir) stdout, como
./useless.sh | sed 's/Output/Useless/'
.Não há problema, basta fazê-lo ANTES de trocar stdout e stderr. Eu recomendo movê-lo para uma função ou arquivo (also-useless.sh) e chamar isso no lugar de ./useless.sh na linha acima.
No entanto, se você deseja CAPTURAR stdout AND stderr, acho que você deve recorrer a arquivos temporários, porque ele
$()
fará apenas um de cada vez e cria um subshell do qual você não pode retornar variáveis.fonte
Iterando um pouco a resposta de Tom Hale, achei possível agrupar o yoga de redirecionamento em uma função para facilitar a reutilização. Por exemplo:
É quase certamente possível simplificar ainda mais isso. Não testei especialmente, mas parece funcionar com o bash e o ksh.
fonte
Se você deseja ignorar o uso de um arquivo temporário, poderá usar a substituição de processo. Ainda não consegui funcionar. Esta foi minha primeira tentativa:
Então eu tentei
Contudo
Portanto, a substituição do processo geralmente está fazendo a coisa certa ... infelizmente, sempre que envolvo o STDIN por dentro
>( )
com algo na$()
tentativa de capturar isso em uma variável, perco o conteúdo de$()
. Eu acho que isso ocorre porque$()
lança um subprocesso que não tem mais acesso ao descritor de arquivo em / dev / fd que pertence ao processo pai.A substituição de processos me deu a capacidade de trabalhar com um fluxo de dados que não está mais no STDERR, infelizmente não parece capaz de manipulá-lo da maneira que desejo.
fonte
./useless.sh 2> >( ERROR=$( cat <() ); echo "$ERROR" )
, veria a saída deERROR
. O problema é que a substituição do processo é executada em um sub-shell, portanto, o valor definido no sub-shell não afeta o shell pai.fonte
a=> b=>stderr
a
é avaliado e atribuído em um sub shell, e a atribuição no sub shell não afeta o shell pai. (Testado no Ubuntu 14.04 LTS e no Mac OS X 10.10.1.)GNU bash, version 4.4.12(1)-release (x86_64-pc-msys)
)SLE 11.4
qualquer um e produz o efeito descrito por @JonathanLefflerNo zsh:
fonte
Para evitar erros em seus comandos:
Inspirado na fabricação Lean:
fonte
if
. Deixe-me postar uma solução separada.Uma solução simples
Vai produzir:
fonte
Melhorando a resposta da YellowApple :
Esta é uma função Bash para capturar stderr em qualquer variável
stderr_capture_example.sh
:Teste:
Resultado:
Esta função pode ser usada para capturar a escolha retornada de um
dialog
comando.fonte