Comment puis-je voir la ligne de commande exacte en cours d'exécution dans une instance bash?

29

J'ai une bashinstance longue (à l'intérieur d'une screensession) qui exécute un ensemble complexe de commandes à l'intérieur d'une boucle (chaque boucle faisant des tuyaux, des redirections, etc.).

La longue ligne de commande a été écrite à l'intérieur du terminal - elle ne se trouve dans aucun script. Maintenant, je connais l'ID du processus bash et j'ai un accès root - comment puis-je voir la ligne de commande exacte en cours d'exécution à l'intérieur de cela bash?

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

Et dans une autre instance de shell, je veux voir la ligne de commande exécutée dans le 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'

Est-ce possible?

EDIT # 1

Ajout de contre-exemples pour certaines réponses que j'ai.

  1. À propos de l'utilisation du cmdlinesous /proc/PID: cela ne fonctionne pas, du moins pas dans mon scénario. Voici un exemple simple:

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

    Dans un autre shell:

    $ cat /proc/8909/cmdline
    bash
  2. L'utilisation ps -p PID --noheaders -o cmdest tout aussi inutile:

    $ ps -p 8909 --no-headers -o cmd
    bash
  3. ps -eaf n'est pas non plus 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

    Autrement dit, il n'y a pas de sortie de la ligne de commande ORIGINALE, ce que je recherche - c'est-à-dire le while true ; do echo 1 ; echo 2>/dev/null ; sleep 30 ; done.

ttsiodras
la source

Réponses:

40

Je savais que je tenais aux pailles, mais UNIX n'échoue jamais!

Voici comment je l'ai géré:

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

Ensuite, à l' (gdb)invite, j'ai exécuté la commande, call write_history("/tmp/foo")qui écrira cet historique dans le fichier /tmp/foo.

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

Je me détache alors du processus.

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

Et arrête gdb.

(gdb) q

Et bien sûr ...

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

Pour une réutilisation future facile, j'ai écrit un script bash , automatisant le processus.

ttsiodras
la source
1
+1 très beau détective.Assurez-vous de marquer cela comme le A excepté dans 2 jours.
slm
@slm: Merci! J'écrirai un blog à ce sujet - c'était amusant, traquer ça.
ttsiodras
1
Avec readline activé , vous pouvez aussi le faire en gdb: print (char *)rl_line_buffer. La commande actuelle dans une séquence est print (char *)the_printed_command. Vous pouvez aussi call history_builtin(), mais cela sortira sur le tty du processus bash, donc cela pourrait être moins utile.
mr.spuratic du
11
@slm: Je n'ai pas pu résister - j'ai blogué à ce sujet ici: users.softlab.ece.ntua.gr/~ttsiod/bashheimer.html
ttsiodras
1
Est bash thread-safe? Vous devrez espérer que vous ne modifiez pas un état interne qui perturbe le code en cours d'exécution lorsque vous exécutez quelque chose à partir de gdb. En pratique, bash attendra presque certainement que son processus enfant se termine chaque fois que vous le suspendez avec gdb.
Adrian Pronk
5

Puisque la commande est toujours en cours d'exécution à l'écran, son parent bash n'a relu aucun historique donc:

  • rattacher à l'écran
  • appuyez ^Zensuiteup arrow
  • bonus: encapsulez la commande entre guillemets simples (en naviguant avec ^A^A- parce que l'écran (1) - et ^E) et la redirection echo + dans un fichier
  • fg poursuivre l'exécution du commandement

Il y a des mises en garde, mais c'est assez utile, la plupart du temps.

Lloeki
la source
Oui, même en le tuant et en le pressant up. Bien que vous deviez être sûr qu'il redémarrera sans aucun problème, mais si ce n'est pas le cas, vous aurez quand même le même problème après un redémarrage. Vous n'avez qu'à choisir votre moment où un temps d'arrêt ne serait pas un problème.
Matthieu Napoli
0

Je sais que vous avez trouvé votre propre réponse, mais y a-t-il une raison pour laquelle vous ne pouvez pas faire quelque chose comme ça:

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

Peut-être que vous ne pouvez pas mélanger la sortie du travail réel et la sortie de bash montrant la ligne en cours d'exécution.

, FWIW, si vous préférez également verbosité, set -o xtrace.

aigrette cramoisie
la source