Como posso ver a linha de comando exata sendo executada em alguma instância do bash?

29

Eu tenho uma bashinstância de execução longa (dentro de uma screensessão) que está executando um conjunto complexo de comandos dentro de um loop (com cada loop executando pipes, redirecionamentos etc.).

A longa linha de comando foi escrita dentro do terminal - não está em nenhum script. Agora, eu sei o ID do processo bash e tenho acesso root - como posso ver a linha de comando exata sendo executada dentro dele bash?

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

E em outra instância do shell, quero ver a linha de comando executada dentro do 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'

Isso é possível?

EDIT # 1

Adicionando contra-exemplos para algumas respostas que tenho.

  1. Sobre o uso do cmdlineunder /proc/PID: isso não funciona, pelo menos não no meu cenário. Aqui está um exemplo simples:

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

    Em outra concha:

    $ cat /proc/8909/cmdline
    bash
  2. Usar ps -p PID --noheaders -o cmdé tão inútil:

    $ ps -p 8909 --no-headers -o cmd
    bash
  3. ps -eaf também não é ú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

    Ou seja, não há saída da linha de comando ORIGINAL, que é o que estou procurando - ou seja, o while true ; do echo 1 ; echo 2>/dev/null ; sleep 30 ; done.

ttsiodras
fonte

Respostas:

40

Eu sabia que estava tentando entender, mas o UNIX nunca falha!

Aqui está como eu consegui:

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

Então, no (gdb)prompt, executei o comando, call write_history("/tmp/foo")que gravará esse histórico no arquivo /tmp/foo.

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

Eu, então, me desapego do processo.

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

E sair gdb.

(gdb) q

E com certeza ...

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

Para facilitar a reutilização futura, escrevi um script bash , automatizando o processo.

ttsiodras
fonte
1
+1 detalhamento muito bom. Certifique-se de marcar isso como o A excluído em 2 dias.
Slm
@ slm: Obrigado! Vou escrever um post sobre isso - foi divertido caçar isso.
Ttsiodras
1
Com readline habilitado, você também pode fazer isso em gdb: print (char *)rl_line_buffer. O comando atual em uma sequência é print (char *)the_printed_command. Você também pode call history_builtin(), mas isso resultará no tty do processo do bash, por isso pode ser menos útil.
mr.spuratic
11
@slm: Não foi possível resistir - eu escrevi sobre isso aqui: users.softlab.ece.ntua.gr/~ttsiod/bashheimer.html
ttsiodras
1
O bash thread-safe? Você terá que esperar que não esteja modificando algum estado interno que perturba o código atualmente em execução quando você executa algo do gdb. Na prática, o bash quase certamente estará apenas aguardando o término do processo filho sempre que você o suspender com gdb.
Adrian Pronk 4/04
5

Como o comando ainda está sendo executado na tela, seu bash pai não releu nenhum histórico, portanto:

  • reconecte à tela
  • pressione ^Zentãoup arrow
  • bônus: coloque o comando entre aspas simples (navegando com ^A^A- porque tela (1) - e ^E) e eco + redirecione para um arquivo
  • fg para perseguir a execução de comandos

Existem advertências, mas isso é útil o suficiente, na maioria das vezes.

Lloeki
fonte
Sim, até matando e pressionando up. Embora você deva ter certeza de que o reinício será iniciado sem problemas, mas se não acontecer, você terá o mesmo problema após uma reinicialização. Você só precisa escolher o momento em que um tempo de inatividade não seria um problema.
Matthieu Napoli
0

Eu sei que você encontrou sua própria resposta, mas há uma razão para você não poder fazer algo assim:

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

Talvez você não consiga combinar a saída do trabalho real e a saída do bash mostrando a linha atualmente em execução.

Além disso, FWIW, se você preferir verbosidade set -o xtrace,.

garça-vermelha
fonte