GNU/Linux >> LINUX-Kenntnisse >  >> Linux

stdout umleiten, während ein Prozess läuft – Was sendet dieser Prozess an /dev/null

Sie können dies mit strace tun.

Mit strace Sie können ausspionieren, was in den Dateideskriptor 1 geschrieben wird, der der stdout-Dateideskriptor ist. Hier ist ein Beispiel:

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' 

Vielleicht möchten Sie den Filter verbessern, aber das wäre eine andere Frage. Wir haben die Ausgabe, müssen sie aber jetzt aufräumen.

:WARNUNG:Diese Lösung hat einige Einschränkungen, siehe Kommentare unten. Es wird nicht immer funktionieren, Ihr Kilometerstand kann variieren.

Test:

Fügen Sie dieses Programm (unten) in die Datei hello ein , und chmod +x hello

#!/bin/bash

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

Dieses in hello1 und chmod +x hello1

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

Dieses in hello2 und chmod +x hello2

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

dann mit ./hello2 >/dev/null ausführen , suchen Sie dann die PID des Prozesses hello und geben Sie pid_of_process_you_want_to_see_stdout_of=xyz ein wobei xyz die PID von hallo ist, dann Zeile oben ausführen.

Wie es funktioniert. Wenn hello ausgeführt wird, bash Forks, leitet fd1 zu /dev/null um , führt dann hello aus. Hello sendet die Ausgabe an fd1 mit dem Systemaufruf write(1, … .Kernel empfängt den Systemaufruf write(1, … , sieht, dass fd 1 mit /dev/null verbunden ist und …

Wir führen dann strace (Systemaufruf-Trace) auf hello aus und sehen, dass es write(1, "hello\nworld\n") aufruft Der Rest der obigen Zeile ist nur die Auswahl der entsprechenden Zeile der Ablaufverfolgung.


Nein. Sie müssen den Befehl neu starten.

Stdio-Handles werden vom übergeordneten an den untergeordneten Prozess vererbt. Sie haben dem Kind ein Handle auf /dev/nul gegeben. Es ist frei, damit zu tun, was es will, einschließlich Dinge wie dup()'ing oder es an seine eigenen Kinder weiterzugeben. Es gibt keinen einfachen Weg, in das Betriebssystem einzudringen und zu ändern, worauf die Handles eines anderen laufenden Prozesses zeigen.

Sie könnten wohl einen Debugger für das untergeordnete Element verwenden und damit beginnen, seinen Zustand zu zappen, alle Speicherorte zu überschreiben, an denen eine Kopie des aktuellen Handle-Werts mit etwas Neuem gespeichert ist, oder seine Aufrufe an den Kernel zu verfolgen und alle E / A zu überwachen. Ich denke, das verlangt den meisten Benutzern viel ab, aber es kann funktionieren, wenn es sich um einen einzelnen untergeordneten Prozess handelt, der nichts Komisches mit der E/A macht.

Aber auch das schlägt im allgemeinen Fall fehl, z. B. ein Skript, das Pipelines und so weiter erstellt, Handles dupliziert und viele eigene Kinder erstellt, die kommen und gehen. Aus diesem Grund müssen Sie so ziemlich von vorne beginnen (und vielleicht auf eine Datei umleiten, die Sie später löschen können, auch wenn Sie sie jetzt nicht ansehen möchten.)


Ich habe ziemlich lange nach der Antwort auf diese Frage gesucht. Es stehen hauptsächlich zwei Lösungen zur Verfügung:

  1. Wie Sie hier gesagt haben, Option strace;
  2. Ausgabe mit gdb abrufen.

In meinem Fall war keine davon zufriedenstellend, da die Ausgabe zuerst abgeschnitten wird (und ich sie nicht länger einstellen konnte). Das zweite kommt nicht in Frage, da auf meiner Plattform gdb nicht installiert ist - es ist ein eingebettetes Gerät.

Durch das Sammeln einiger Teilinformationen im Internet (habe sie nicht erstellt, nur Teile zusammengefügt) erreichte ich die Lösung mithilfe von Named Pipes (FIFOs). Wenn der Prozess ausgeführt wird, wird seine Ausgabe an die benannte Pipe geleitet, und wenn niemand sie sehen möchte, wird ein dummer Listener (tail -f>> /dev/null) darauf angewendet, um den Puffer zu leeren. Wenn jemand diese Ausgabe erhalten möchte, wird der Tail-Prozess beendet (andernfalls wird die Ausgabe zwischen den Lesern gewechselt) und ich cat die Pipe. Nach dem Hören beginnt ein weiterer Schweif.

Mein Problem bestand also darin, einen Prozess zu starten, die SSH-Shell zu beenden und sich dann erneut anzumelden und die Ausgabe abzurufen. Dies ist jetzt mit folgenden Befehlen möglich:

#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>"

Das Skript, das dies bewerkstelligt, ist unten angehängt. Ich bin mir bewusst, dass es keine perfekte Lösung ist. Bitte teilen Sie Ihre Gedanken mit, vielleicht ist es hilfreich.

#!/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. Wie behandelt Linux mehrere aufeinanderfolgende Pfadtrennzeichen (/home////username///file)?

  2. Linux – Was bedeutet der Buchstabe „u“ in /dev/urandom?

  3. Was sind /dev/zero- und /dev/null-Dateien in Linux

  4. So leiten Sie die Ausgabe einer Anwendung im Hintergrund nach /dev/null um

  5. DD von /dev/zero nach /dev/null ... was eigentlich passiert

Was ist „/dev/null 2&1“ unter Linux

So leiten Sie die Ausgabe unter Linux nach /dev/null um

Was ist /dev/null unter Linux

Wann sollte ich /dev/shm/ verwenden und wann sollte ich /tmp/?

echo oder print /dev/stdin /dev/stdout /dev/stderr

Warum sind < oder > erforderlich, um /dev/tcp