Rediriger STDERR / STDOUT d'un processus APRES qu'il a été démarré, en utilisant la ligne de commande?

127

Dans le shell, vous pouvez effectuer une redirection, > <etc., mais qu'en est-il APRÈS qu'un programme ait démarré?

Voici comment j'en suis venu à poser cette question, un programme s'exécutant en arrière-plan de mon terminal continue de produire du texte ennuyeux. C'est un processus important donc je dois ouvrir un autre shell pour éviter le texte. J'aimerais pouvoir >/dev/nullou une autre redirection afin de pouvoir continuer à travailler dans le même shell.

Ian Kelling
la source
Je sais que le moyen le plus simple de rediriger le STDOUT / STDERR est de DUP2 leurs descripteurs de fichier AVANT de bifurquer. C'est une pratique assez courante, et probablement la façon dont les obus le font actuellement. Je ne sais pas si cela donne une réponse, mais je pense que cela diminue les chances qu'il y en ait une bonne.
Stefan Mai
1
reptyr
Louis

Réponses:

124

À moins de fermer et de rouvrir votre tty (c'est-à-dire se déconnecter et se reconnecter, ce qui peut également mettre fin à certains de vos processus d'arrière-plan dans le processus), vous n'avez plus qu'un choix:

  • attachez-vous au processus en question à l'aide de gdb et exécutez:
    • p dup2 (ouvert ("/ dev / null", 0), 1)
    • p dup2 (ouvert ("/ dev / null", 0), 2)
    • détacher
    • quitter

par exemple:

$ 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

Vous pouvez également envisager:

  • en utilisant screen; écran fournit plusieurs TTY virtuels auxquels vous pouvez basculer sans avoir à ouvrir de nouvelles sessions SSH / telnet / etc
  • en utilisant nohup; cela vous permet de fermer et de rouvrir votre session sans perdre aucun processus d'arrière-plan dans le processus ...
vladr
la source
1
Votre réponse gdb ne fonctionnait pas avec le fichier tail -f, et elle ne fonctionnait pas avec un programme de test en c compilé avec gcc -ggdb qui effectue un printf toutes les secondes. De plus, cont rend impossible d'exécuter plus de commandes gdb, la commande serait detach, puis quit.
Ian Kelling
Correct pour détacher, il est 2h du matin. :) Qu'est-ce qui n'a pas fonctionné exactement avec la solution gdb?
vladr
12
Si vous redirigez stdout / stderr (vers autre chose que / dev / null apparemment), vous devez ouvrir le fichier avec un accès en écriture - open("/path/to/new/stdout",O_WRONLY). O_WRONLY ne sera probablement pas disponible, cependant; sa valeur est 1sur Linux / glibc.
Jander
13
Un mot d'avertissement: l'attachement à un processus dans gdb met le processus en pause jusqu'à ce que vous vous en détachaiez.
Marty B
1
Ajouter au commentaire de @Jander, en utilisant 1025active O_APPENDen plus de O_WRONLY, ce qui est pratique si vous redirigez à la fois stderr et stdout vers le même fichier.
spectres du
57

Cela fera:

strace -ewrite -p $PID

Ce n'est pas si propre (affiche des lignes comme:) write(#,<text you want to see>), mais ça marche!


Vous pourriez également ne pas aimer le fait que les arguments soient abrégés. Pour contrôler cela, utilisez le -sparamètre qui définit la longueur maximale des chaînes affichées.

Il capture tous les flux, vous pouvez donc filtrer cela d'une manière ou d'une autre:

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

affiche uniquement les appels du descripteur 1. 2>&1est de rediriger STDERR vers STDOUT, comme straceécrit dans STDERR par défaut.

Naugtur
la source
6
Ce n'est pas ce que le PO a demandé. OP a demandé de REDIRECTER loin du TTY, pas d'intercepter. De plus, sur certaines plates-formes, strace / truss insérera des espaces entre les caractères de flux interceptés et / ou échappera non-ASCII, et vous devrez également traiter ces derniers.
vladr le
4
Oui, cela fait partiellement la chose - mais pour certaines personnes qui lisent cette question, c'est tout ce dont elles ont besoin - pour voir ce qui se passe dans un programme exécuté par erreur pour écrire sur null ou sur une autre console. Je l'ai découvert après avoir trouvé cette question dans le processus et j'ai pensé que c'était un bon hack (du moins pour moi). ET pas mal de gens trouvent utile si mes yeux ne me trompent pas;)
naugtur
Il est également possible que ce sudosoit nécessaire.
colidyre
21

riffing de l'excellente recherche de vladr (et d'autres):

créez les deux fichiers suivants dans le même répertoire, quelque chose dans votre chemin, par exemple $ HOME / bin:

silence.gdb, contenant (d'après la réponse de vladr):


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

et silence, contenant:


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

Maintenant, la prochaine fois que vous oubliez de rediriger firefox, par exemple, et votre terminal commence à être encombré par les inévitables messages "(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

Vous pouvez également rediriger la sortie de gdb vers / dev / null si vous ne voulez pas la voir.

jcomeau_ictx
la source
3
Mon gdb (v7.2) a une option pratique --batch-silentqui supprime la sortie et ne vous décharge pas dans la console gdb si quelque chose ne va pas (par exemple, processus manquant). BTW, $!fait référence au travail d'arrière-plan le plus récent, mais je ne pense pas qu'il puisse être utilisé dans le script lui-même. J'utilise un alias: alias silencebg='silence $!'
seanf
18

Rediriger la sortie d'un processus en cours vers un autre terminal, fichier ou écran:

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

À l'intérieur de gdb :

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

Détachez un processus en cours d'exécution du terminal bash et gardez-le en vie:

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

Explication:

20818 - juste un exemple de l'exécution du processus pid
p - imprimer le résultat de la commande gdb
close (1) - fermer la sortie standard
/ dev / pts / 4 - terminal à écrire pour
fermer (2) - fermer la sortie d'erreur
/ tmp / myerrlog - fichier vers écrire dans
q - quitter gdb
bg% 1 - exécuter le travail 1 arrêté en arrière-plan
désavouer% 1 - détacher le travail 1 du terminal

Mirek
la source
2
Cela ne fonctionnera pas si stdin(descripteur de fichier 0) est fermé.
pabouk
Cela m'a sauvé la journée. J'ai eu une création en cours dans une session ssl prenant une heure pour les 10 premiers% et je ne voulais vraiment pas que mon ordinateur portable fonctionne pendant encore 10 heures. Mais ai-je raison de supposer que votre redirection pour stderr doit être lue p open("/tmp/myerrlog", 2)?
GerardV
Il y avait un problème très mineur avec cette exécution sur CentOS 6 - le fichier "/ tmp / myerrlog" devait déjà exister. C'était trivial de le créer avec le toucher, bien sûr.
ebneter
3

Ce n'est pas une réponse directe à votre question, mais c'est une technique que j'ai trouvée utile ces derniers jours: exécutez la commande initiale en utilisant 'screen', puis détachez.

Roger Lipscombe
la source
2

il s'agit d'une partie de script bash basée sur les réponses précédentes, qui redirige le fichier journal lors de l'exécution d'un processus ouvert, elle est utilisée comme postscript dans le logrotateprocessus

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

Vous pouvez utiliser reredirect ( https://github.com/jerome-pouiller/reredirect/ ).

Type

reredirect -m FILE PID

et les sorties (standard et erreur) seront écrites dans FILE.

reredirect README explique également comment restaurer l'état d'origine du processus, comment rediriger vers une autre commande ou rediriger uniquement stdout ou stderr.

reredirectfournir également un script appelé relinkqui permet de rediriger vers le terminal actuel:

relink PID
relink PID | grep usefull_content

(reredirect semble avoir les mêmes fonctionnalités que Dupx décrit dans une autre réponse mais, cela ne dépend pas de Gdb).

Jérôme Pouiller
la source