GNU/Linux >> Linux の 問題 >  >> Linux

プロセスの実行中に stdout をリダイレクトする - そのプロセスが /dev/null に送信するのは何ですか

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 つのソリューションが利用可能です:

<オール>
  • ここで述べたように、strace オプション;
  • gdb を使用して出力を取得する。
  • 私の場合、最初に出力が切り捨てられるため、どれも満足のいくものではありませんでした(そして、それ以上設定することはできませんでした)。私のプラットフォームには 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;
    

    Linux
    1. Linuxは複数の連続したパスセパレーター(/ home //// username /// file)をどのように処理しますか?

    2. / dev / stdin、/ dev / stdout、および/ dev / stderrはどの程度移植可能ですか?

    3. Linux の /dev/zero および /dev/null ファイルとは

    1. バックグラウンドでアプリケーションの出力を /dev/null にリダイレクトする方法

    2. 出力を /dev/null にリダイレクトしても、printf にはコストがかかりますか?

    3. /dev/shm/ と /tmp/ はいつ使用する必要がありますか?

    1. /dev/zero から /dev/null への DD ...実際に何が起こるか

    2. Linux:/dev/console 、 /dev/tty 、 /dev/tty0 の違い

    3. /dev/null に書き込む仮想ブロック デバイスを作成します。