¿Cómo puedo ver la línea de comando exacta que se ejecuta dentro de alguna instancia de bash?

29

Tengo una bashinstancia de larga ejecución (dentro de una screensesión) que está ejecutando un conjunto complejo de comandos dentro de un bucle (con cada bucle haciendo canalizaciones, redireccionamientos, etc.).

La larga línea de comando se escribió dentro del terminal; no está dentro de ningún script. Ahora, conozco el ID del proceso bash y tengo acceso a la raíz: ¿cómo puedo ver la línea de comando exacta que se ejecuta dentro de eso bash?

Ejemplo
bash$ echo $$
1234
bash$ while true ; do \
    someThing | somethingElse 2>/foo/bar | \
    yetAnother ; sleep 600 ; done

Y en otra instancia de shell, quiero ver la línea de comando ejecutada dentro del PID 1234:

bash$ echo $$
5678
bash$ su -
sh# cd /proc/1234
sh# # Do something here that will display the string  \
   'while true ; do someThing | somethingElse 2>/foo/bar | \
    yetAnother ; sleep 600 ; done'

es posible?

EDITAR # 1

Agregar contraejemplos para algunas respuestas que tengo.

  1. Sobre el uso de cmdlineunder /proc/PID: eso no funciona, al menos no en mi escenario. Aquí hay un ejemplo simple:

    $ echo $$
    8909
    
    $ while true ; do echo 1 ; echo 2>/dev/null ; sleep 30 ; done

    En otro caparazón:

    $ cat /proc/8909/cmdline
    bash
  2. Usar ps -p PID --noheaders -o cmdes igual de inútil:

    $ ps -p 8909 --no-headers -o cmd
    bash
  3. ps -eaf tampoco es útil:

    $ ps -eaf | grep 8909
    ttsiod    8909  8905  0 10:09 pts/0    00:00:00 bash
    ttsiod   30697  8909  0 10:22 pts/0    00:00:00 sleep 30
    ttsiod   31292 13928  0 10:23 pts/12   00:00:00 grep --color=auto 8909

    Es decir, no hay salida de la línea de comando ORIGINAL, que es lo que estoy buscando, es decir, el while true ; do echo 1 ; echo 2>/dev/null ; sleep 30 ; done.

ttsiodras
fuente

Respuestas:

40

Sabía que estaba agarrando pajitas, ¡pero UNIX nunca falla!

Así es como lo logré:

bash$ gdb --pid 8909
...
Loaded symbols for /lib/i386-linux-gnu/i686/cmov/libnss_files.so.2
0xb76e7424 in __kernel_vsyscall ()

Luego, en el (gdb)indicador, ejecuté el comando, call write_history("/tmp/foo")que escribirá este historial en el archivo /tmp/foo.

(gdb) call write_history("/tmp/foo")
$1 = 0

Luego me separo del proceso.

(gdb) detach
Detaching from program: /bin/bash, process 8909

Y renunciar gdb.

(gdb) q

Y efectivamente ...

bash$ tail -1 /tmp/foo
while true ; do echo 1 ; echo 2>/dev/null ; sleep 30 ; done

Para una fácil reutilización futura, escribí un script bash , automatizando el proceso.

ttsiodras
fuente
1
+1 muy buena investigación. Asegúrese de marcar esto como la excepción A en 2 días.
slm
@slm: ¡Gracias! Escribiré una publicación de blog al respecto: fue divertido, cazar esto.
ttsiodras
1
Con readline activado también se puede hacer esto en gdb: print (char *)rl_line_buffer. El comando actual en una secuencia es print (char *)the_printed_command. También puede call history_builtin(), pero eso se generará en el tty del proceso bash, por lo que podría ser menos útil.
Sr.Spuratic
11
@slm: No pude resistirme: escribí en un
ttsiodras
1
¿Bash es seguro para subprocesos? Tendrá que esperar que no esté modificando algún estado interno que altere el código que se está ejecutando actualmente cuando ejecuta algo desde gdb. En la práctica, bash seguramente esperará a que finalice su proceso hijo cada vez que lo suspendas con gdb.
Adrian Pronk
5

Como el comando todavía se está ejecutando en la pantalla, su bash principal no ha vuelto a leer ningún historial, por lo que:

  • volver a conectar a la pantalla
  • presione ^Zentoncesup arrow
  • bono: ajuste el comando entre comillas simples (navegando con ^A^A- porque pantalla (1) - y ^E) y echo + redirigir a un archivo
  • fg perseguir la ejecución del comando

Hay advertencias, pero esto es bastante útil, la mayoría de las veces.

Lloeki
fuente
Sí, incluso matándolo y presionando up. Aunque debe estar seguro de que comenzará nuevamente sin ningún problema, pero si no lo hace, tendría el mismo problema después de un reinicio de todos modos. Solo tiene que elegir su momento cuando un tiempo de inactividad no sería un problema.
Matthieu Napoli
0

Sé que encontraste tu propia respuesta, pero hay una razón por la que no puedes hacer algo como esto:

(set -x; for f in 1 2 3 4 ; do  echo "$f"; sleep $f; done)

Quizás no pueda mezclar la salida del trabajo real y la salida de bash que muestra la línea que se está ejecutando actualmente.

Además, pedía algo, si lo prefiere la verbosidad, set -o xtrace.

garceta carmesí
fuente