GNU/Linux >> Tutoriales Linux >  >> Linux

Redirigir stdout mientras se ejecuta un proceso:¿qué está enviando ese proceso a/dev/null?

Puedes hacerlo usando strace.

Usando strace puede espiar lo que se escribe en el descriptor de archivo 1, que es el descriptor de archivo estándar. Aquí hay un ejemplo:

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' 

Es posible que desee mejorar el filtro, pero esa sería otra cuestión. Tenemos la salida, pero ahora tenemos que arreglarla.

:ADVERTENCIA:Esta solución tiene algunas limitaciones, consulte los comentarios a continuación. No siempre funcionará, su millaje puede variar.

Prueba:

Pon este programa (abajo) en el archivo hello y chmod +x hello

#!/bin/bash

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

Este en hello1 y chmod +x hello1

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

Este en hello2 y chmod +x hello2

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

luego ejecuta con ./hello2 >/dev/null , luego busque el pid del proceso hello y escriba pid_of_process_you_want_to_see_stdout_of=xyz donde xyz es el pid de hola, luego ejecute la línea en la parte superior.

Cómo funciona. Cuando se ejecuta hello, bash bifurca, redirige fd1 a /dev/null , luego execs hello.Hello envía la salida a fd1 usando la llamada al sistema write(1, … .Kernel recibe una llamada al sistema write(1, … , ve que fd 1 está conectado a /dev/null y…

Luego ejecutamos strace (rastreo de llamada del sistema) en hello, y vemos que está llamando a write(1, "hello\nworld\n") El resto si la línea de arriba es solo seleccionar la línea apropiada del trazo.


No. Deberá reiniciar el comando.

Los identificadores de Stdio se heredan del proceso principal al secundario. Le ha dado al niño un identificador para /dev/nul. Es libre de hacer con él lo que quiera, incluidas cosas como duplicarlo () o pasárselo a sus propios hijos. No hay una manera fácil de acceder al sistema operativo y cambiar a lo que apuntan los identificadores de otro proceso en ejecución.

Podría decirse que podría usar un depurador en el hijo y comenzar a eliminar su estado, sobrescribiendo cualquier ubicación donde esté almacenada una copia del valor del identificador actual con algo nuevo, o para rastrear sus llamadas al kernel, monitoreando cualquier i/o. Creo que es mucho pedir a la mayoría de los usuarios, pero puede funcionar si se trata de un único proceso secundario que no hace nada extraño con la E/S.

Pero incluso eso falla en el caso general, por ejemplo, una secuencia de comandos que crea canalizaciones y demás, engañando a los identificadores y creando muchos de sus propios elementos secundarios que van y vienen. Esta es la razón por la que está prácticamente atascado con comenzar de nuevo (y tal vez redirigir a un archivo que puede eliminar más tarde, incluso si no desea verlo ahora).


Estuve buscando la respuesta a esta pregunta durante bastante tiempo. Existen principalmente dos soluciones disponibles:

  1. Como dijiste aquí, selecciona la opción;
  2. Obteniendo la salida usando gdb.

En mi caso, ninguno de ellos fue saisfactory, porque primero trunca la salida (y no pude configurarlo por más tiempo). El segundo está fuera de discusión, ya que mi plataforma no tiene gdb instalado, es un dispositivo integrado.

Recopilando información parcial en Internet (no la creé, solo junté las piezas), llegué a la solución usando canalizaciones con nombre (FIFO). Cuando se ejecuta el proceso, su salida se dirige a la tubería nombrada y si nadie quiere verla, se le aplica un oyente tonto (tail -f>> /dev/null) para vaciar el búfer. Cuando alguien quiere obtener esta salida, el proceso de cola se cancela (de lo contrario, la salida se alterna entre los lectores) y atrapo la tubería. Al terminar de escuchar, se levanta otra cola.

Entonces, mi problema era iniciar un proceso, salir del shell ssh y luego iniciar sesión una vez más y poder obtener el resultado. Esto es factible ahora con los siguientes comandos:

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

El script que logra eso se adjunta a continuación. Soy consciente de que no es una solución perfecta. Por favor, comparta sus pensamientos, tal vez sea útil.

#!/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. ¿Cómo maneja Linux múltiples separadores de rutas consecutivas (/home////username///file)?

  2. Linux:¿Diferencia entre /dev/console, /dev/tty y /dev/tty0?

  3. ¿Qué tan portátiles son /dev/stdin, /dev/stdout y /dev/stderr?

  4. ¿Evitar que un comando envíe Stdout y Stderr a cualquier terminal o archivo?

  5. Linux:¿Qué significa la letra 'u' en /dev/urandom?

¿Qué es '/dev/null 2&1' en Linux?

Cómo redirigir la salida a /dev/null en Linux

¿Qué es /dev/null en Linux?

Redirigir stderr a /dev/null

¿printf seguirá teniendo un costo incluso si redirijo la salida a/dev/null?

¿Por qué se requieren < o > para usar /dev/tcp?