¿Redirigir STDERR / STDOUT de un proceso DESPUÉS de que se haya iniciado, utilizando la línea de comando?

127

En el shell puede hacer redirección, > <etc., pero ¿qué tal DESPUÉS de que se inicie un programa?

Así es como llegué a hacer esta pregunta, un programa que se ejecuta en el fondo de mi terminal sigue generando texto molesto. Es un proceso importante, así que tengo que abrir otro shell para evitar el texto. Me gustaría poder >/dev/nullo alguna otra redirección para poder seguir trabajando en el mismo shell.

Ian Kelling
fuente
Sé que la forma más fácil de redirigir el STDOUT / STDERR es DUP2 sus descriptores de archivo ANTES de bifurcar. Esta es una práctica bastante estándar, y probablemente la forma en que los shells lo logran en este momento. No estoy seguro de si eso da una respuesta, pero creo que disminuye las posibilidades de que haya una buena.
Stefan Mai
1
reptyr
Louis

Respuestas:

124

Antes de cerrar y volver a abrir su tty (es decir, cerrar sesión y volver a iniciarla, lo que también puede terminar algunos de sus procesos en segundo plano en el proceso) solo le queda una opción:

  • adjunte al proceso en cuestión usando gdb y ejecute:
    • p dup2 (abierto ("/ dev / null", 0), 1)
    • p dup2 (abierto ("/ dev / null", 0), 2)
    • despegar
    • dejar

p.ej:

$ 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

También puede considerar:

  • utilizando screen; La pantalla proporciona varios TTY virtuales entre los que puede cambiar sin tener que abrir nuevas sesiones SSH / telnet / etc.
  • utilizando nohup; Esto le permite cerrar y volver a abrir su sesión sin perder ningún proceso en segundo plano en el ... proceso.
vladr
fuente
1
Su respuesta gdb no funcionó con el archivo tail -f, y no funcionó con un programa de prueba en c compilado con gcc -ggdb que realiza una impresión cada segundo. También cont hace que sea imposible ejecutar más comandos gdb, el comando se separaría y luego se cerraría.
Ian Kelling
Correcto sobre la separación, son las 2AM. :) ¿Qué no funcionó exactamente con la solución gdb?
vladr
12
Si está redirigiendo stdout / stderr (a algo además de / dev / null aparentemente), debe abrir el archivo con acceso de escritura - open("/path/to/new/stdout",O_WRONLY). Sin embargo, O_WRONLY probablemente no estará disponible; su valor está 1en Linux / glibc.
Jander
13
Una palabra de precaución: adjuntar a un proceso en gdb detiene el proceso hasta que se separe de él.
Marty B
1
Agregando al comentario de @Jander, usando 1025activa O_APPENDademás de O_WRONLY, lo cual es útil si redirige stderr y stdout al mismo archivo.
espectros
57

Esto lo hara:

strace -ewrite -p $PID

No es tan limpio (muestra líneas como:) write(#,<text you want to see>), ¡pero funciona!


También es posible que no le guste el hecho de que los argumentos se abrevian. Para controlar eso, use el -sparámetro que establece la longitud máxima de las cadenas que se muestran.

Captura todas las transmisiones, por lo que es posible que desee filtrar eso de alguna manera:

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

muestra solo descriptor 1 llamadas. 2>&1es redirigir STDERR a STDOUT, como se straceescribe en STDERR de forma predeterminada.

naugtur
fuente
66
Esto no es lo que solicitó el OP. OP solicitó REDIRECER lejos del TTY, no interceptar. Además, en algunas plataformas, strace / truss insertará espacios entre los caracteres de flujo interceptados y / o escapará fuera de ASCII, y también tendrá que lidiar con el procesamiento de esos.
vladr
44
Sí, esto hace la cosa parcialmente, pero para algunas personas que leen esta pregunta es todo lo que necesitan: ver lo que está sucediendo en un programa ejecutado por error para escribir en nulo o en otra consola. Lo descubrí después de encontrar esta pregunta en el proceso y pensé que era un buen truco (al menos para mí). Y a algunas personas les resulta útil si mis ojos no me
engañan
También es posible que sudosea ​​necesario.
colidyre
21

analizando la excelente investigación de vladr (y de otros):

cree los siguientes dos archivos en el mismo directorio, algo en su ruta, diga $ HOME / bin:

silencio.gdb, que contiene (de la respuesta de vladr):


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

y silencio, que contiene:


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

Ahora, la próxima vez que se olvide de redirigir Firefox, por ejemplo, y su terminal comienza a saturarse con los inevitables mensajes "(firefox-bin: 5117): Gdk-WARNING **: colisión XID, problemas por delante":


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

También puede redirigir la salida de gdb a / dev / null si no desea verla.

jcomeau_ictx
fuente
3
Mi gdb (v7.2) tiene una práctica opción --batch-silentque suprime la salida y no lo descarga en la consola de gdb si algo sale mal (por ejemplo, falta un proceso). Por cierto, se $!refiere al trabajo de fondo más reciente, pero no creo que pueda usarse en el script en sí. Yo uso un alias: alias silencebg='silence $!'
seanf
18

Redirigir la salida de un proceso en ejecución a otro terminal, archivo o pantalla:

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

Dentro de gdb :

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

Separe un proceso en ejecución del terminal bash y manténgalo vivo:

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

Explicación:

20818 - solo un ejemplo de proceso en ejecución pid
p - imprimir resultado del comando gdb
close (1) - cerrar salida estándar
/ dev / pts / 4 - terminal para escribir para
cerrar (2) - cerrar salida de error
/ tmp / myerrlog - archivo para escriba en
q - salga de gdb
bg% 1 - ejecute el trabajo detenido 1 en segundo plano
% 1 - desconecte el trabajo 1 del terminal

Mirek
fuente
2
Esto no funcionará si stdin(descriptor de archivo 0) está cerrado.
pabouk
Esto me salvó el día. Tuve una marca en ejecución en una sesión SSL que tomó una hora durante el primer 10% y realmente no quería mantener mi computadora portátil funcionando durante otras 10 horas. ¿Pero estoy en lo cierto al suponer que su redirección para stderr debería leer p open("/tmp/myerrlog", 2)?
GerardV
Tuve un problema muy pequeño con esto ejecutándose en CentOS 6: el archivo "/ tmp / myerrlog" ya debía existir. Era trivial crearlo con tacto, por supuesto.
Ebneter
3

No es una respuesta directa a su pregunta, pero es una técnica que he encontrado útil en los últimos días: ejecute el comando inicial usando 'pantalla' y luego desconecte.

Roger Lipscombe
fuente
2

Esta es una parte del script bash basada en respuestas anteriores, que redirige el archivo de registro durante la ejecución de un proceso abierto, se utiliza como postdata en logrotateproceso

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

Puede usar reredirect ( https://github.com/jerome-pouiller/reredirect/ ).

Tipo

reredirect -m FILE PID

y las salidas (estándar y error) se escribirán en ARCHIVO.

reredirect README también explica cómo restaurar el estado original del proceso, cómo redirigir a otro comando o redirigir solo stdout o stderr.

reredirectTambién proporciona un script llamado relinkque permite redirigir al terminal actual:

relink PID
relink PID | grep usefull_content

(reredirect parece tener las mismas características que Dupx descrito en otra respuesta, pero no depende de Gdb).

Jérôme Pouiller
fuente