Come posso vedere l'esatta riga di comando eseguita all'interno di qualche istanza di bash?


29

Ho bashun'istanza di lunga durata (all'interno di una screensessione) che esegue un insieme complesso di comandi all'interno di un ciclo (con ogni ciclo che esegue pipe, reindirizzamenti, ecc.).

La lunga riga di comando è stata scritta all'interno del terminale - non è contenuta in nessuno script. Ora conosco l'ID del processo bash e ho accesso root - come posso vedere l'esatta riga di comando eseguita all'interno di quello bash?

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

E in un'altra istanza della shell, voglio vedere la riga di comando eseguita all'interno di 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'

È possibile?

EDIT # 1

Aggiunta di contro-esempi per alcune risposte che ho.

  1. Informazioni sull'uso di cmdlineunder /proc/PID: non funziona, almeno non nel mio scenario. Ecco un semplice esempio:

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

    In un'altra shell:

    $ cat /proc/8909/cmdline
    bash
  2. L'uso ps -p PID --noheaders -o cmdè altrettanto inutile:

    $ ps -p 8909 --no-headers -o cmd
    bash
  3. ps -eaf inoltre non è utile:

    $ 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

    Cioè, non c'è alcun output della riga di comando ORIGINALE, che è quello che sto cercando - cioè il while true ; do echo 1 ; echo 2>/dev/null ; sleep 30 ; done.

Risposte:


40

Sapevo che stavo afferrando le cannucce, ma UNIX non fallisce mai!

Ecco come l'ho gestito:

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

Quindi al (gdb)prompt ho eseguito il comando, call write_history("/tmp/foo")che scriverà questa cronologia nel file /tmp/foo.

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

Quindi mi stacco dal processo.

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

E smettila gdb.

(gdb) q

E abbastanza sicuro ...

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

Per un facile riutilizzo futuro, ho scritto uno script bash , automatizzando il processo.


1
+1 investigazione molto bella. Assicurati di contrassegnarlo come A esclusa in 2 giorni.
slm

@slm: grazie! Scriverò un post sul blog - è stato divertente, dare la caccia a questo.
ttsiodras,

1
Con readline abilitato si può anche fare questo in gdb: print (char *)rl_line_buffer. Il comando corrente in una sequenza è print (char *)the_printed_command. Puoi anche call history_builtin(), ma questo verrà generato sulla tty del processo bash, quindi potrebbe essere meno utile.
mr.spuratic,

11
@slm: non ho potuto resistere - ho scritto sul blog qui: users.softlab.ece.ntua.gr/~ttsiod/bashheimer.html
ttsiodras

1
Bash è thread-safe? Dovrai sperare di non modificare alcuni stati interni che sconvolgono il codice attualmente in esecuzione quando esegui qualcosa da gdb. In pratica, bash quasi sicuramente aspetterà che il suo processo figlio finisca ogni volta che ti capita di sospenderlo con gdb.
Adrian Pronk,

5

Poiché il comando è ancora in esecuzione sullo schermo, il suo bash padre non ha riletto alcuna cronologia quindi:

  • ricollegare allo schermo
  • premere ^Zquindiup arrow
  • bonus: racchiudi il comando tra virgolette singole (navigando con ^A^A- because screen (1) - and ^E) ed echo + reindirizzamento in un file
  • fg per eseguire l'esecuzione del comando

Ci sono avvertenze, ma questo è abbastanza utile, il più delle volte.


Sì, anche uccidendolo e premendo up. Anche se devi essere sicuro che ricomincerà senza alcun problema, ma se non lo fa avresti comunque lo stesso problema dopo un riavvio. Devi solo scegliere il tuo momento in cui un downtime non sarebbe un problema.
Matthieu Napoli,

0

So che hai trovato la tua risposta, ma c'è un motivo per cui non puoi fare qualcosa del genere:

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

Forse non è possibile combinare l'output del lavoro effettivo e l'output di bash che mostra la riga attualmente in esecuzione.

Inoltre, FWIW, se si preferisce la verbosità, set -o xtrace.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.