GNU/Linux >> Belajar Linux >  >> Linux

Redirect stdout saat proses sedang berjalan - Proses apa yang dikirim ke /dev/null

Anda dapat melakukannya menggunakan strace.

Menggunakan strace Anda dapat memata-matai apa yang sedang ditulis ke file-descriptor 1, yang merupakan deskriptor file stdout. Ini contohnya:

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' 

Anda mungkin ingin meningkatkan filter, tetapi itu akan menjadi pertanyaan lain. Kami memiliki hasilnya, tetapi sekarang perlu merapikannya.

:PERINGATAN:Solusi ini memiliki beberapa keterbatasan, lihat komentar di bawah. Ini tidak selalu berhasil, jarak tempuh Anda mungkin berbeda.

Uji:

Taruh program ini (di bawah) di file hello , dan chmod +x hello

#!/bin/bash

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

Yang ini di hello1 dan chmod +x hello1

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

Yang ini di hello2 dan chmod +x hello2

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

lalu jalankan dengan ./hello2 >/dev/null , lalu cari pid dari process hello dan ketik pid_of_process_you_want_to_see_stdout_of=xyz di mana xyz adalah pid dari halo, lalu jalankan baris di atas.

Cara kerjanya. Saat halo dijalankan, bash forks, alihkan fd1 ke /dev/null , lalu jalankan hello.Hello mengirimkan output ke fd1 menggunakan system call write(1, … .Kernel menerima system call write(1, … , lihat bahwa fd 1 terhubung ke /dev/null dan …

Kami kemudian menjalankan strace (system-call trace) di halo, dan melihat bahwa itu memanggil write(1, "hello\nworld\n") Selebihnya jika baris di atas tinggal memilih sesuai garis jejaknya.


Tidak. Anda harus memulai ulang perintah.

Pegangan stdio diwariskan dari proses induk ke anak. Anda telah memberi anak pegangan ke /dev/nul. Ini bebas untuk melakukan apa pun yang disukainya, termasuk hal-hal seperti dup()'ing atau memberikannya kepada anak-anaknya sendiri. Tidak ada cara mudah untuk menjangkau ke dalam OS dan mengubah apa yang ditunjuk oleh pegangan proses lain yang sedang berjalan.

Bisa dibilang, Anda bisa menggunakan debugger pada anak dan mulai mengubah statusnya, menimpa lokasi mana pun yang menyimpan salinan nilai pegangan saat ini dengan sesuatu yang baru, atau untuk melacak panggilannya ke kernel, memantau i/o apa pun. Saya rasa itu menanyakan banyak hal kepada sebagian besar pengguna, tetapi ini dapat berhasil jika proses anak tunggal yang tidak melakukan sesuatu yang lucu dengan i/o.

Tetapi bahkan itu gagal dalam kasus umum, misalnya, skrip yang membuat saluran pipa dan sebagainya, menangani pembodohan dan membuat banyak turunannya sendiri yang datang dan pergi. Inilah sebabnya mengapa Anda sering terjebak untuk memulai dari awal (dan mungkin mengalihkan ke file yang dapat Anda hapus nanti meskipun Anda tidak ingin menontonnya sekarang.)


Saya mencari jawaban untuk pertanyaan ini cukup lama. Ada dua solusi utama yang tersedia:

  1. Seperti yang Anda nyatakan di sini, opsi strace;
  2. Mendapatkan keluaran menggunakan gdb.

Dalam kasus saya, tidak satu pun dari mereka yang memuaskan, karena pertama-tama memotong output (dan saya tidak dapat mengaturnya lebih lama). Yang kedua keluar dari pertanyaan, karena platform saya tidak menginstal gdb - ini adalah perangkat yang disematkan.

Mengumpulkan beberapa informasi parsial di Internet (tidak membuatnya, hanya menyatukannya), saya mencapai solusi menggunakan pipa bernama (FIFO). Saat proses dijalankan, keluarannya diarahkan ke pipa bernama dan jika tidak ada yang ingin melihatnya, pendengar bodoh (tail -f>> /dev/null) diterapkan padanya untuk mengosongkan buffer. Ketika seseorang ingin mendapatkan keluaran ini, proses ekor dimatikan (jika tidak, keluarannya berganti-ganti antara pembaca) dan saya cat pipanya. Saat mendengarkan selesai, ekor lain dimulai.

Jadi masalah saya adalah memulai proses, keluar dari shell ssh, lalu login sekali lagi dan bisa mendapatkan hasilnya. Ini bisa dilakukan sekarang dengan perintah berikut:

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

Skrip yang melakukannya terlampir di bawah ini. Saya menyadari bahwa itu bukan solusi yang sempurna. Silakan, bagikan pemikiran Anda, mungkin itu akan membantu.

#!/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. Bagaimana Linux Menangani Beberapa Pemisah Jalur Berturut-turut (/home////username///file)?

  2. Seberapa Portabel /dev/stdin, /dev/stdout Dan /dev/stderr?

  3. Apa itu file /dev/zero dan /dev/null di Linux?

  1. Cara mengarahkan keluaran aplikasi di latar belakang ke /dev/null

  2. DD dari /dev/zero ke /dev/null...apa yang sebenarnya terjadi

  3. Linux:Perbedaan antara /dev/console , /dev/tty dan /dev/tty0

  1. Linux:Perbedaan Antara /dev/console , /dev/tty Dan /dev/tty0?

  2. Mencegah Perintah Mengirim Stdout &Stderr ke Terminal Atau File Apa Pun?

  3. Buat perangkat blok virtual yang menulis ke /dev/null