strace を使用して実行できます。
strace
の使用 stdout ファイル記述子であるファイル記述子 1 に書き込まれている内容をスパイできます。以下に例を示します:
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'
フィルターを改善したいかもしれませんが、それは別の問題です。出力はできましたが、次は整理する必要があります。
:警告:このソリューションにはいくつかの制限があります。以下のコメントを参照してください。常に機能するとは限りません。マイレージは異なる場合があります。
テスト:
このプログラム (以下) をファイル hello
に入れます 、および chmod +x hello
#!/bin/bash
while true
do
echo -en "hello\nworld\n"
done
これは hello1
にあります と chmod +x hello1
#!/bin/bash
dir=$(dirname $0)
$dir/hello >/dev/null
これは hello2
にあります そして chmod +x hello2
#!/bin/bash
dir=$(dirname $0)
$dir/hello1 >/dev/null
次に ./hello2 >/dev/null
で実行します 次に、プロセス hello の pid を見つけて、pid_of_process_you_want_to_see_stdout_of=xyz
と入力します。 ここで、xyz は hello の pid で、行を先頭で実行します。
仕組み.hello が実行されると、bash がフォークし、fd1 を /dev/null
にリダイレクトします。 hello.Hello は、システム コール write(1, …
を使用して出力を fd1 に送信します。 .Kernel はシステムコール write(1, …
を受け取ります 、 fd 1 が /dev/null
に接続されていることがわかります そして…
次に、hello で strace (システム コール トレース) を実行し、write(1, "hello\nworld\n")
を呼び出していることを確認します。 上記の行がトレースの適切な行を選択しているだけの場合の残り。
いいえ。コマンドを再起動する必要があります。
Stdio ハンドルは、親プロセスから子プロセスに継承されます。子に /dev/nul へのハンドルを与えました。 dup() したり、自分の子供に渡したりするなど、好きなことを自由に行うことができます。 OS にアクセスして、別の実行中のプロセスのハンドルが指すものを変更する簡単な方法はありません。
おそらく、子でデバッガーを使用してその状態のザッピングを開始し、現在のハンドル値のコピーが保存されている場所を新しいもので上書きするか、カーネルへの呼び出しを追跡して i/o を監視することができます。ほとんどのユーザーはそれを求めていると思いますが、I/O でおかしなことをしない単一の子プロセスであれば機能します。
しかし、それも一般的なケースでは失敗します。たとえば、パイプラインなどを作成し、ハンドルを複製し、出入りする独自の子を多数作成するスクリプトなどです。これが、最初からやり直すことにほとんど行き詰まっている理由です (そして、今は見たくない場合でも、後で削除できるファイルにリダイレクトする可能性があります)。
私はかなり長い間、この質問に対する答えを探していました。主に 2 つのソリューションが利用可能です:
<オール>私の場合、最初に出力が切り捨てられるため、どれも満足のいくものではありませんでした(そして、それ以上設定することはできませんでした)。私のプラットフォームには gdb がインストールされていないため、2 番目は問題外です。これは組み込みデバイスです。
インターネットで部分的な情報を収集して (作成したのではなく、断片をまとめただけです)、名前付きパイプ (FIFO) を使用するソリューションにたどり着きました。プロセスが実行されると、その出力は名前付きパイプに送信され、誰もそれを見たくない場合は、ダム リスナー (tail -f>> /dev/null) が適用されてバッファーが空になります。誰かがこの出力を取得したい場合、テール プロセスが強制終了され (そうでない場合、出力はリーダー間で交互に行われます)、パイプを猫で囲みます。リスニングが終わると、別のしっぽが始まります。
したがって、私の問題は、プロセスを開始し、sshシェルを終了してから、もう一度ログに記録して出力を取得できるようにすることでした。これは、次のコマンドで実行できるようになりました:
#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>"
それを実現するスクリプトを以下に添付します。完全な解決策ではないことは承知しています。あなたの考えを共有してください。
#!/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;