GNU/Linux >> Tutoriels Linux >  >> Linux

Rediriger stdout pendant qu'un processus est en cours d'exécution - Qu'est-ce que ce processus envoie à /dev/null

Vous pouvez le faire en utilisant strace.

Utilisation de strace vous pouvez espionner ce qui est écrit dans le descripteur de fichier 1, qui est le descripteur de fichier stdout. Voici un exemple :

strace  -p $pid_of_process_you_want_to_see_stdout_of 2>&1 | \
    sed -re 's%^write\(1,[[:blank:]](.*),[[:blank:]]*[0-9]+\)[[:blank:]]*=[[:blank:]]*[0-9]+%\1%g' 

Vous voudrez peut-être améliorer le filtre, mais ce serait une autre question. Nous avons la sortie, mais nous devons maintenant la ranger.

:ATTENTION :Cette solution présente certaines limitations, voir les commentaires ci-dessous. Cela ne fonctionnera pas toujours, votre kilométrage peut varier.

Test :

Mettez ce programme (ci-dessous) dans le fichier hello , et chmod +x hello

#!/bin/bash

while true
do
    echo -en  "hello\nworld\n"
done

Celui-ci en hello1 et chmod +x hello1

#!/bin/bash
dir=$(dirname $0)
$dir/hello >/dev/null

Celui-ci en hello2 et chmod +x hello2

#!/bin/bash
dir=$(dirname $0)
$dir/hello1 >/dev/null

puis exécutez avec ./hello2 >/dev/null , puis trouvez le pid du processus hello et tapez pid_of_process_you_want_to_see_stdout_of=xyz où xyz est le pid de hello, puis exécutez la ligne en haut.

Comment ça marche.Lorsque hello est exécuté, bash fork, redirige fd1 vers /dev/null , puis execs hello.Hello envoie la sortie à fd1 en utilisant l'appel système write(1, … .Kernel reçoit l'appel système write(1, … , voit que fd 1 est connecté à /dev/null et …

Nous exécutons ensuite strace (system-call trace) sur hello, et voyons qu'il appelle write(1, "hello\nworld\n") Le reste si la ligne ci-dessus ne fait que sélectionner la ligne appropriée de la trace.


Non. Vous devrez redémarrer la commande.

Les poignées Stdio sont héritées du processus parent au processus enfant. Vous avez donné à l'enfant un handle vers /dev/nul. Il est libre d'en faire ce qu'il veut, y compris des choses comme le duper() ou le transmettre à ses propres enfants. Il n'y a pas de moyen facile d'accéder au système d'exploitation et de modifier ce vers quoi pointent les descripteurs d'un autre processus en cours d'exécution.

On peut dire que vous pouvez utiliser un débogueur sur l'enfant et commencer à zapper son état, en écrasant tous les emplacements où il est stocké une copie de la valeur actuelle du handle avec quelque chose de nouveau, ou pour tracer ses appels au noyau, en surveillant toute entrée/sortie. Je pense que cela demande beaucoup à la plupart des utilisateurs, mais cela peut fonctionner s'il s'agit d'un seul processus enfant qui ne fait rien de bizarre avec les entrées/sorties.

Mais même cela échoue dans le cas général, par exemple, un script qui crée des pipelines, etc., en dupant les poignées et en créant beaucoup de ses propres enfants qui vont et viennent. C'est pourquoi vous êtes pratiquement obligé de recommencer (et peut-être de rediriger vers un fichier que vous pourrez supprimer plus tard, même si vous ne voulez pas le regarder maintenant.)


J'ai longtemps cherché la réponse à cette question. Il existe principalement deux solutions :

  1. Comme vous l'avez indiqué ici, l'option strace ;
  2. Obtenir la sortie à l'aide de gdb.

Dans mon cas, aucun d'entre eux n'était saisfactory, car tronque d'abord la sortie (et je ne pouvais pas la définir plus longtemps). La seconde est hors de question, car ma plate-forme n'a pas installé gdb - c'est un périphérique intégré.

En collectant des informations partielles sur Internet (je ne les ai pas créées, j'ai juste assemblé des morceaux), j'ai atteint la solution en utilisant des canaux nommés (FIFO). Lorsque le processus est exécuté, sa sortie est dirigée vers le tube nommé et si personne ne veut le voir, un écouteur muet (tail -f>> /dev/null) lui est appliqué pour vider le tampon. Lorsque quelqu'un veut obtenir cette sortie, le processus de queue est tué (sinon la sortie est alternée entre les lecteurs) et je cat le tuyau. À la fin de l'écoute, une autre queue démarre.

Mon problème était donc de démarrer un processus, de quitter le shell ssh, puis de vous reconnecter et de pouvoir obtenir la sortie. C'est faisable maintenant avec les commandes suivantes :

#start the process in the first shell
./runner.sh start "<process-name-with-parameters>"&
#exit the shell
exit

#start listening in the other shell
./runner listen "<process-name-params-not-required>"
#
#here comes the output
#
^C

#listening finished. If needed process may be terminated - scripts ensures the clean up
./runner.sh stop "<process-name-params-not-required>"

Le script qui accomplit cela est joint ci-dessous. Je suis conscient que ce n'est pas une solution parfaite. S'il vous plaît, partagez vos pensées, cela sera peut-être utile.

#!/bin/sh

## trapping functions
trap_with_arg() {
    func="$1" ; shift
    for sig ; do
        trap "$func $sig" "$sig"
    done
}

proc_pipe_name() {
    local proc=$1;
    local pName=/tmp/kfifo_$(basename ${proc%%\ *});
    echo $pName;
}

listener_cmd="tail -f";
func_start_dummy_pipe_listener() {
    echo "Starting dummy reader";
    $listener_cmd $pipeName >> /dev/null&
}

func_stop_dummy_pipe_listener() {
    tailPid=$(func_get_proc_pids "$listener_cmd $pipeName");
    for pid in $tailPid; do
        echo "Killing proc: $pid";
        kill $tailPid;
    done;
}

func_on_stop() {
        echo "Signal $1 trapped. Stopping command and cleaning up";
    if [ -p "$pipeName" ]; then
        echo "$pipeName existed, deleting it";
        rm $pipeName;
    fi;



    echo "Cleaning done!";
}

func_start_proc() {
    echo "Something here"
    if [ -p $pipeName ]; then
        echo "Pipe $pipeName exists, delete it..";
        rm $pipeName;
    fi;
    mkfifo $pipeName;

    echo "Trapping INT TERM & EXIT";
    #trap exit to do some cleanup
    trap_with_arg func_on_stop INT TERM EXIT

    echo "Starting listener";
    #start pipe reader cleaning the pipe
    func_start_dummy_pipe_listener;

    echo "Process about to be started. Streaming to $pipeName";
    #thanks to this hack, the process doesn't  block on the pipe w/o readers
    exec 5<>$pipeName
    $1 >&5 2>&1
    echo "Process done";
}

func_get_proc_pids() {
    pids="";
    OIFS=$IFS;
    IFS='\n';
    for pidline in $(ps -A -opid -ocomm -oargs | grep "$1" | grep -v grep); do
        pids="$pids ${pidline%%\ *}";
    done;
    IFS=$OIFS;
    echo ${pids};
}

func_stop_proc() {
    tailPid=$(func_get_proc_pids "$this_name start $command");
    if [ "_" == "_$tailPid" ]; then
        echo "No process stopped. The command has to be exactly the same command (parameters may be ommited) as when started.";
    else
        for pid in $tailPid; do
            echo "Killing pid $pid";
            kill $pid;
        done;
    fi;
}

func_stop_listening_to_proc() {
    echo "Stopped listening to the process due to the $1 signal";
    if [ "$1" == "EXIT" ]; then
        if [ -p "$pipeName" ]; then
            echo "*Restarting dummy listener"; 
            func_start_dummy_pipe_listener;
        else 
            echo "*No pipe $pipeName existed";
        fi;
    fi;
}

func_listen_to_proc() {
    #kill `tail -f $pipeName >> /dev/null`
    func_stop_dummy_pipe_listener;

    if [ ! -p $pipeName ]; then 
        echo "Can not listen to $pipeName, exitting...";
        return 1;
    fi;

    #trap the kill signal to start another tail... process
    trap_with_arg func_stop_listening_to_proc INT TERM EXIT
    cat $pipeName;
    #NOTE if there is just an end of the stream in a pipe, we have to do nothing 

}

#trap_with_arg func_trap INT TERM EXIT

print_usage() {
    echo "Usage $this_name [start|listen|stop] \"<command-line>\"";
}

######################################3
############# Main entry #############
######################################

this_name=$0;
option=$1;
command="$2";
pipeName=$(proc_pipe_name "$command");


if [ $# -ne 2 ]; then
    print_usage;
    exit 1;
fi;

case $option in 
start)
    echo "Starting ${command}";
    func_start_proc "$command";
    ;;
listen)
    echo "Listening to ${2}";
    func_listen_to_proc "$command";
    ;;
stop)
    echo "Stopping ${2}";
    func_stop_proc "$command";
    ;;
*)
    print_usage;
    exit 1;
esac;

Linux
  1. Linux :Différence entre /dev/console , /dev/tty et /dev/tty0 ?

  2. Empêcher une commande d'envoyer à la fois Stdout et Stderr à n'importe quel terminal ou fichier ?

  3. Que sont les fichiers /dev/zero et /dev/null sous Linux

  4. Rediriger stderr vers /dev/null

  5. Est-ce que printf aura toujours un coût même si je redirige la sortie vers /dev/null ?

Qu'est-ce que '/dev/null 2&1' sous Linux

Comment rediriger la sortie vers /dev/null sous Linux

Qu'est-ce que /dev/null sous Linux

Comment rediriger la sortie d'une application en arrière-plan vers /dev/null

Shell :redirige stdout vers /dev/null et stderr vers stdout

Créer un périphérique de bloc virtuel qui écrit dans /dev/null