Reindirizzare STDERR / STDOUT di un processo DOPO che è stato avviato, utilizzando la riga di comando?


127

Nella shell è possibile eseguire il reindirizzamento, > <ecc., Ma che ne dici di DOPO l'avvio di un programma?

Ecco come sono arrivato a porre questa domanda, un programma in esecuzione sullo sfondo del mio terminale continua a inviare testo fastidioso. È un processo importante, quindi devo aprire un'altra shell per evitare il testo. Mi piacerebbe essere in grado >/dev/nullo qualche altro reindirizzamento in modo da poter continuare a lavorare nella stessa shell.


Conosco il modo più semplice per reindirizzare STDOUT / STDERR è DUP2 i loro descrittori di file PRIMA di eseguire il fork. Questa è una pratica abbastanza standard, e probabilmente il modo in cui le shell la realizzano proprio ora. Non sono sicuro che dia una risposta, ma sto pensando che diminuisce le possibilità che ci sia una buona risposta.
Stefan Mai,

Risposte:


124

A meno di chiudere e riaprire il tuo tty (es. Disconnettersi e riaccendere, che potrebbe anche terminare alcuni dei tuoi processi in background nel processo) ti rimane solo una scelta:

  • collegarsi al processo in questione utilizzando gdb ed eseguire:
    • p dup2 (aperto ("/ dev / null", 0), 1)
    • p dup2 (aperto ("/ dev / null", 0), 2)
    • Staccare
    • smettere

per esempio:

$ 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

Puoi anche considerare:

  • utilizzando screen ; schermo fornisce diversi TTY virtuali tra cui è possibile passare senza dover aprire nuove sessioni SSH / telnet / ecc
  • usando nohup; questo ti consente di chiudere e riaprire la sessione senza perdere alcun processo in background nel processo ...

1
La tua risposta gdb non ha funzionato con il file tail -f e non ha funzionato con un programma di test in c compilato con gcc -ggdb che esegue un printf ogni secondo. Inoltre, rende impossibile eseguire più comandi gdb, il comando sarebbe staccare, quindi uscire.
Ian Kelling,

Corretto sul distacco, sono le 2 del mattino. :) Cosa non ha funzionato esattamente con la soluzione gdb?
Vladr,

12
Se stai reindirizzando stdout / stderr (apparentemente a qualsiasi cosa oltre a / dev / null), devi aprire il file con accesso in scrittura - open("/path/to/new/stdout",O_WRONLY). O_WRONLY probabilmente non sarà disponibile, comunque; il suo valore è 1su Linux / glibc.
Jander,

13
Un avvertimento: l'attaccamento a un processo in gdb mette in pausa il processo fino a quando non si stacca da esso.
Marty B,

1
Aggiungendo il commento di @Jander, usando 1025activates O_APPENDin aggiunta a O_WRONLY, che è utile se si reindirizza sia stderr che stdout allo stesso file.
extra il

57

Questo farà:

strace -ewrite -p $PID

Non è così pulito (mostra linee come:) write(#,<text you want to see>), ma funziona!


Potresti anche non gradire il fatto che gli argomenti siano abbreviati. Per controllare quell'uso il-s parametro che imposta la lunghezza massima delle stringhe visualizzate.

Cattura tutti i flussi, quindi potresti voler filtrare in qualche modo:

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

mostra solo le chiamate al descrittore 1. 2>&1consiste nel reindirizzare STDERR su STDOUT, come stracescrive STDERR per impostazione predefinita.


6
Questo non è ciò che l'OP ha richiesto. OP ha chiesto di REDIRECT lontano dal TTY, non intercettare. Inoltre, su alcune piattaforme strace / truss inserirà spazi tra caratteri di stream intercettati e / o escape non-ASCII, e dovrai occuparti anche di elaborarli.
Vladr,

4
Sì, questo fa in parte - ma per alcune persone che leggono questa domanda è tutto ciò di cui hanno bisogno - per vedere cosa sta succedendo in un programma erroneamente eseguito per scrivere su null o su un'altra console. L'ho scoperto dopo aver trovato questa domanda nel processo e ho pensato che fosse un bel trucco (almeno per me). E un bel po 'di persone lo trovano utile se i miei occhi non mi
ingannano

È anche possibile che sudosia necessario.
Colidyre,

21

rifondando l'eccellente ricerca di Vladr (e di altri):

crea i seguenti due file nella stessa directory, qualcosa nel tuo percorso, ad esempio $ HOME / bin:

silence.gdb, contenente (dalla risposta di Vladr):


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

e silenzio, contenente:


#!/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

Ora, la prossima volta che ti dimentichi di reindirizzare firefox, per esempio, e il tuo terminale inizia a essere ingombra di messaggi inevitabili "(firefox-bin: 5117): Gdk-WARNING **: XID collision, trouble ahead":


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

Puoi anche reindirizzare l'output di gdb su / dev / null se non vuoi vederlo.


3
Il mio gdb (v7.2) ha un'utile opzione --batch-silentche sopprime l'output e non ti scarica nella console di gdb se qualcosa va storto (es. Processo mancante). A proposito, si $!riferisce al lavoro in background più recente, ma non penso che possa essere utilizzato nello script stesso. Uso un alias: alias silencebg='silence $!'
seanf

18

Reindirizzare l'output da un processo in esecuzione su un altro terminale, file o schermo:

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

All'interno di gdb :

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

Stacca un processo in esecuzione dal terminale bash e mantienilo in vita:

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

Spiegazione:

20818 - solo un esempio di pid di processo in esecuzione
- stampa il risultato del comando gdb
chiudi (1) - chiudi output standard
/ dev / pts / 4 - terminale per scrivere per
chiudere (2) - chiudi output errore
/ tmp / myerrlog - file in scrivi su
q - esci da gdb
bg% 1 - esegue il lavoro interrotto 1 su sfondo
disown% 1 - scollega il lavoro 1 dal terminale


2
Questo non funzionerà se stdin(descrittore di file 0) è chiuso.
pabouk,

Questo mi ha salvato la giornata. In una sessione ssl ho fatto un giro di corsa che impiegava un'ora per il primo 10% e non volevo davvero mantenere il mio portatile acceso per altre 10 ore. Ma ho ragione a supporre che il tuo reindirizzamento per stderr dovrebbe leggere p open("/tmp/myerrlog", 2)?
GerardV,

Aveva un problema molto piccolo con questo in esecuzione su CentOS 6 - il file "/ tmp / myerrlog" doveva già esistere. Naturalmente è stato banale crearlo con il tocco.
febbraio

3

Non è una risposta diretta alla tua domanda, ma è una tecnica che ho trovato utile negli ultimi giorni: esegui il comando iniziale usando 'schermo', quindi staccalo.


2

questa è una parte di script bash basata su risposte precedenti, che reindirizza il file di registro durante l'esecuzione di un processo aperto, viene utilizzato come postscript nel 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


0

È possibile utilizzare reredirect ( https://github.com/jerome-pouiller/reredirect/ ).

genere

reredirect -m FILE PID

e le uscite (standard ed errore) saranno scritte in FILE.

reredirect README spiega anche come ripristinare lo stato originale del processo, come reindirizzare a un altro comando o reindirizzare solo stdout o stderr.

reredirectfornisce anche uno script chiamato relinkche consente di reindirizzare al terminale corrente:

relink PID
relink PID | grep usefull_content

(reredirect sembra avere le stesse funzionalità di Dupx descritte in un'altra risposta, ma non dipende da Gdb).

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.