GNU/Linux >> Znalost Linux >  >> Linux

Přesměrování stdout za běhu procesu -- Co tento proces odesílá do /dev/null

Můžete to udělat pomocí strace.

Pomocí strace můžete špehovat, co se zapisuje do deskriptoru souboru 1, což je deskriptor souboru stdout. Zde je příklad:

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' 

Možná budete chtít filtr vylepšit, ale to by byla jiná otázka. Výstup máme, ale teď ho musíme uklidit.

:UPOZORNĚNÍ:Toto řešení má určitá omezení, viz komentáře níže. Ne vždy to bude fungovat, váš počet najetých kilometrů se může lišit.

Test:

Vložte tento program (níže) do souboru hello a chmod +x hello

#!/bin/bash

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

Tento v hello1 a chmod +x hello1

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

Tento v hello2 a chmod +x hello2

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

pak spusťte s ./hello2 >/dev/null ,potom najděte pid procesu hello a napište pid_of_process_you_want_to_see_stdout_of=xyz kde xyz je pid z hello, pak spustit řádek nahoře.

Jak to funguje. Když se spustí hello, bash se rozvětví, přesměruje fd1 na /dev/null , pak provede hello.Hello odešle výstup na fd1 pomocí systémového volání write(1, … .Kernel přijme systémové volání write(1, … , vidí, že fd 1 je připojen k /dev/null a …

Poté spustíme strace (trasování systémového volání) na hello a uvidíme, že volá write(1, "hello\nworld\n") Zbytek, pokud řádek výše pouze vybírá vhodný řádek trasování.


Ne. Budete muset restartovat příkaz.

Úchyty Stdio se dědí z nadřazeného na podřízený proces. Dali jste dítěti popisovač /dev/nul. Může si s ním dělat, co chce, včetně věcí, jako je dup()'ing nebo předání jeho vlastním dětem. Neexistuje žádný snadný způsob, jak se dostat do operačního systému a změnit to, na co ukazují úchyty jiného běžícího procesu.

Pravděpodobně byste mohli použít debugger na potomka a začít přepínat jeho stav, přepisovat všechna umístění, kde je uložena kopie aktuální hodnoty handle, něčím novým, nebo sledovat jeho volání do jádra a monitorovat jakékoli vstupy a výstupy. Myslím, že to žádá spousta většiny uživatelů, ale může to fungovat, pokud jde o proces s jedním potomkem, který s i/o nedělá nic vtipného.

Ale i to selhává v obecném případě, např. skript, který vytváří potrubí a tak dále, dupá rukojeti a vytváří spoustu vlastních dětí, které přicházejí a odcházejí. To je důvod, proč jste do značné míry uvízli v tom, že začínáte znovu (a možná přesměrováváte na soubor, který můžete později smazat, i když jej nyní nechcete sledovat.)


Odpověď na tuto otázku jsem hledal poměrně dlouho. K dispozici jsou především dvě řešení:

  1. Jak jste zde uvedli, možnost strace;
  2. Získání výstupu pomocí gdb.

V mém případě nebyl žádný z nich uspokojivý, protože nejprve zkrátil výstup (a nemohl jsem ho nastavit déle). Druhý nepřipadá v úvahu, protože moje platforma nemá nainstalovanou gdb - je to embedded zařízení.

Shromážděním některých dílčích informací na internetu (nevytvořil jsem je, jen jsem je dal dohromady) jsem dosáhl řešení pomocí pojmenovaných rour (FIFO). Když je proces spuštěn, jeho výstup je směrován do pojmenované roury a pokud jej nikdo nechce vidět, použije se na něj hloupý posluchač (tail -f>> /dev/null), aby se vyprázdnil buffer. Když někdo chce získat tento výstup, ukončí se proces ocasu (jinak se výstup střídá mezi čtečkami) a já kačkuji. Po dokončení poslechu se spustí další ocas.

Můj problém byl tedy spustit proces, ukončit ssh shell a pak se znovu přihlásit a být schopen získat výstup. To je nyní proveditelné pomocí následujících příkazů:

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

Skript, který toho dosáhne, je přiložen níže. Jsem si vědom toho, že to není dokonalé řešení. Prosím, podělte se o své myšlenky, možná to bude užitečné.

#!/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. Jak Linux zpracovává více po sobě jdoucích oddělovačů cest (/home////username///soubor)?

  2. Zabránit odeslání příkazu Stdout i Stderr do libovolného terminálu nebo souboru?

  3. Jak přesměrovat výstup aplikace na pozadí do /dev/null

  1. Shell:přesměrujte stdout na /dev/null a stderr na stdout

  2. Přesměrujte stderr na /dev/null

  3. Bude mít printf stále cenu, i když přesměruji výstup do /dev/null?

  1. DD z /dev/zero do /dev/null...co se vlastně stane

  2. Linux:Rozdíl mezi /dev/console , /dev/tty a /dev/tty0

  3. echo nebo print /dev/stdin /dev/stdout /dev/stderr