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:
- Wie Sie hier gesagt haben, Option strace;
- 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;