Redirecionar STDERR / STDOUT de um processo APÓS o início, usando a linha de comando?

127

No shell, você pode fazer o redirecionamento > <etc., mas que tal APÓS o início de um programa?

Aqui está como vim fazer essa pergunta: um programa em execução no fundo do meu terminal continua emitindo texto irritante. É um processo importante, então eu tenho que abrir outro shell para evitar o texto. Eu gostaria de poder>/dev/null ou algum outro redirecionamento para continuar trabalhando no mesmo shell.

Ian Kelling
fonte
Eu sei que a maneira mais fácil de redirecionar o STDOUT / STDERR é DUP2 seus descritores de arquivo ANTES de bifurcar. Essa é uma prática bastante padrão e, provavelmente, da maneira que as conchas a realizam agora. Não tenho certeza se isso dá uma resposta, mas acho que diminui as chances de haver uma boa.
Stefan Mai
1
Reptyr
Louis

Respostas:

124

Antes de fechar e reabrir seu tty (por exemplo, fazer logoff e logon novamente, o que também pode encerrar alguns de seus processos em segundo plano no processo), você só tem uma opção:

  • anexe ao processo em questão usando o gdb e execute:
    • p dup2 (aberto ("/ dev / null", 0), 1)
    • p dup2 (aberto ("/ dev / null", 0), 2)
    • destacar
    • Sair

por exemplo:

$ tail -f /var/log/lastlog &
[1] 5636

$ ls -l /proc/5636/fd
total 0
lrwx------ 1 myuser myuser 64 Feb 27 07:36 0 -> /dev/pts/0
lrwx------ 1 myuser myuser 64 Feb 27 07:36 1 -> /dev/pts/0
lrwx------ 1 myuser myuser 64 Feb 27 07:36 2 -> /dev/pts/0
lr-x------ 1 myuser myuser 64 Feb 27 07:36 3 -> /var/log/lastlog

$ gdb -p 5636
GNU gdb 6.8-debian
Copyright (C) 2008 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Attaching to process 5636
Reading symbols from /usr/bin/tail...(no debugging symbols found)...done.
Reading symbols from /lib/librt.so.1...(no debugging symbols found)...done.
Loaded symbols for /lib/librt.so.1
Reading symbols from /lib/libc.so.6...(no debugging symbols found)...done.
Loaded symbols for /lib/libc.so.6
Reading symbols from /lib/libpthread.so.0...(no debugging symbols found)...done.
[Thread debugging using libthread_db enabled]
[New Thread 0x7f3c8f5a66e0 (LWP 5636)]
Loaded symbols for /lib/libpthread.so.0
Reading symbols from /lib/ld-linux-x86-64.so.2...(no debugging symbols found)...done.
Loaded symbols for /lib64/ld-linux-x86-64.so.2

(no debugging symbols found)
0x00007f3c8eec7b50 in nanosleep () from /lib/libc.so.6

(gdb) p dup2(open("/dev/null",0),1)
[Switching to Thread 0x7f3c8f5a66e0 (LWP 5636)]
$1 = 1

(gdb) p dup2(open("/dev/null",0),2)
$2 = 2

(gdb) detach
Detaching from program: /usr/bin/tail, process 5636

(gdb) quit

$ ls -l /proc/5636/fd
total 0
lrwx------ 1 myuser myuser 64 Feb 27 07:36 0 -> /dev/pts/0
lrwx------ 1 myuser myuser 64 Feb 27 07:36 1 -> /dev/null
lrwx------ 1 myuser myuser 64 Feb 27 07:36 2 -> /dev/null
lr-x------ 1 myuser myuser 64 Feb 27 07:36 3 -> /var/log/lastlog
lr-x------ 1 myuser myuser 64 Feb 27 07:36 4 -> /dev/null
lr-x------ 1 myuser myuser 64 Feb 27 07:36 5 -> /dev/null

Você também pode considerar:

  • usando screen; A tela fornece vários TTYs virtuais com os quais você pode alternar sem precisar abrir novas sessões SSH / telnet / etc,
  • usando nohup; isso permite que você feche e reabra sua sessão sem perder nenhum processo em segundo plano no processo.
vladr
fonte
1
Sua resposta ao gdb não funcionou com o arquivo tail -f e não funcionou com um programa de teste em c compilado com gcc -ggdb que imprime a cada segundo. Além disso, cont impossibilita a execução de mais comandos gdb, o comando seria desanexado e depois encerrado.
26230 Ian Kelling
Correto ao desanexar, são 2 da manhã. :) O que exatamente não funcionou com a solução gdb?
vladr
12
Se você estiver redirecionando stdout / stderr (para algo além de / dev / null, aparentemente), será necessário abrir o arquivo com acesso de gravação - open("/path/to/new/stdout",O_WRONLY). O_WRONLY provavelmente não estará disponível; seu valor está 1no Linux / glibc.
Jander
13
Uma palavra de cautela: anexar a um processo no gdb pausa o processo até você se desconectar dele.
Marty B
1
Adicionando em comentário 's @Jander, usando 1025ativa O_APPEND, além de O_WRONLY, que é útil se você redirecionar tanto stderr e stdout para o mesmo arquivo.
spectras 17/10/16
57

Isso fará:

strace -ewrite -p $PID

Não é tão limpo (mostra linhas como:) write(#,<text you want to see>), mas funciona!


Você também pode não gostar do fato de que os argumentos são abreviados. Para controlar isso, use o -sparâmetro que define o comprimento máximo de cadeias exibidas.

Ele captura todos os fluxos, então você pode filtrar isso de alguma forma:

strace -ewrite -p $PID 2>&1 | grep "write(1" 

mostra apenas as chamadas do descritor 1. 2>&1é redirecionar STDERR para STDOUT, como straceescreve para STDERR por padrão.

naugtur
fonte
6
Não foi isso que o OP pediu. O OP pediu para se redirecionar para longe do TTY, não para interceptar. Além disso, em algumas plataformas, strace / truss inserirá espaços entre caracteres de fluxo interceptados e / ou escapará de não-ASCII, e você terá que lidar com o processamento deles também.
vladr 4/09
4
Sim, isso funciona parcialmente - mas para algumas pessoas que lêem essa pergunta é tudo o que precisam - para ver o que está acontecendo em um programa executado por engano para gravar em null ou em outro console. Descobri depois de encontrar essa pergunta no processo e achei que era um bom truque (pelo menos para mim). E muitas pessoas acham útil se meus olhos não me
enganarem
Também é possível que sudoseja necessário.
Colidyre 30/03/19
21

riffing excelente pesquisa de vladr (e outros):

crie os dois arquivos a seguir no mesmo diretório, algo no seu caminho, digamos $ HOME / bin:

silence.gdb, contendo (da resposta de vladr):


p dup2(open("/dev/null",0),1)
p dup2(open("/dev/null",0),2)
detach
quit

e silêncio, contendo:


#!/bin/sh
if [ "$0" -a "$1" ]; then
 gdb -p $1 -x $0.gdb
else
 echo Must specify PID of process to silence >&2
fi

chmod +x ~/bin/silence  # make the script executable

Agora, da próxima vez que você esquecer de redirecionar o firefox, por exemplo, e seu terminal começar a ficar cheio de mensagens inevitáveis ​​"(firefox-bin: 5117): Gdk-WARNING **: colisão XID, problemas à frente":


ps  # look for process xulrunner-stub (in this case we saw the PID in the error above)
silence 5117  # run the script, using PID we found

Você também pode redirecionar a saída do gdb para / dev / null se não quiser vê-la.

jcomeau_ictx
fonte
3
Meu gdb (v7.2) tem uma opção útil --batch-silentque suprime a saída e não o despeja no console do gdb se algo der errado (por exemplo, processo ausente). BTW, $!refere-se ao trabalho em segundo plano mais recente, mas acho que não pode ser usado no próprio script. Eu uso um apelido: alias silencebg='silence $!'
seanf 12/02/2013
18

Redirecione a saída de um processo em execução para outro terminal, arquivo ou tela:

tty
ls -l /proc/20818/fd
gdb -p 20818

Dentro do gdb :

p close(1)
p open("/dev/pts/4", 1)
p close(2)
p open("/tmp/myerrlog", 1)
q

Desanexe um processo em execução do terminal bash e mantenha-o vivo:

[Ctrl+z]
bg %1 && disown %1
[Ctrl+d]

Explicação:

20818 - apenas um exemplo do processo em execução pid
p - imprima o resultado do comando gdb
close (1) - feche a saída padrão
/ dev / pts / 4 - terminal para gravar no
fechamento (2) - feche a saída de erro
/ tmp / myerrlog - arquivo para gravar em
q - sair do gdb
bg% 1 - executar o trabalho parado 1 no fundo
rejeitado% 1 - desanexar o trabalho 1 do terminal

Mirek
fonte
2
Isso não funcionará se stdin(descritor de arquivo 0) estiver fechado.
Pabouk
Isso salvou meu dia. Eu tinha uma marca de execução em uma sessão SSL, demorando uma hora nos primeiros 10% e realmente não queria manter meu laptop funcionando por mais 10 horas. Mas estou certo em assumir que seu redirecionamento para stderr deve ser lido p open("/tmp/myerrlog", 2)?
GerardV
Teve um problema muito pequeno com esta execução no CentOS 6 - o arquivo "/ tmp / myerrlog" já deveria existir. Era trivial criá-lo com toque, é claro.
ebneter 6/01/19
3

Não é uma resposta direta à sua pergunta, mas é uma técnica que eu tenho achado útil nos últimos dias: execute o comando inicial usando 'screen' e, em seguida, desanexe.

Roger Lipscombe
fonte
2

esta é a parte do script bash com base nas respostas anteriores, que redirecionam o arquivo de log durante a execução de um processo aberto, é usado como postscript no logrotateprocesso

#!/bin/bash

pid=$(cat /var/run/app/app.pid)
logFile="/var/log/app.log"

reloadLog()
{
    if [ "$pid" = "" ]; then
        echo "invalid PID"
    else
        gdb -p $pid >/dev/null 2>&1 <<LOADLOG
p close(1)
p open("$logFile", 1)
p close(2)
p open("$logFile", 1)
q
LOADLOG
        LOG_FILE=$(ls /proc/${pid}/fd -l | fgrep " 1 -> " | awk '{print $11}')
        echo "log file set to $LOG_FILE"
    fi
}

reloadLog
Mostafa Nazari
fonte
0

Você pode usar o reredirect ( https://github.com/jerome-pouiller/reredirect/ ).

Tipo

reredirect -m FILE PID

e as saídas (padrão e erro) serão gravadas em FILE.

O README da reredirect também explica como restaurar o estado original do processo, como redirecionar para outro comando ou redirecionar apenas stdout ou stderr.

reredirectTambém forneça um script chamado relinkque permite redirecionar para o terminal atual:

relink PID
relink PID | grep usefull_content

(O reredirect parece ter os mesmos recursos que o Dupx descrito em outra resposta, mas isso não depende do Gdb).

Jérôme Pouiller
fonte