Jak mogę zobaczyć dokładną linię poleceń wykonywaną w jakiejś instancji bash?


29

Mam długo działającą bashinstancję (wewnątrz screensesji), która wykonuje złożony zestaw poleceń w pętli (każda pętla wykonuje potoki, przekierowania itp.).

Długa linia poleceń została napisana wewnątrz terminala - nie ma jej w żadnym skrypcie. Teraz znam identyfikator procesu bash i mam dostęp do konta root - jak mogę zobaczyć, jaki dokładnie wiersz poleceń jest w nim wykonywany bash?

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

W innej instancji powłoki chcę zobaczyć, jak wiersz poleceń jest wykonywany w 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'

czy to możliwe?

EDYCJA 1

Dodanie kontrprzykładów dla niektórych odpowiedzi, które mam.

  1. O korzystaniu z cmdlineunder /proc/PID: to nie działa, przynajmniej nie w moim scenariuszu. Oto prosty przykład:

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

    W innej powłoce:

    $ cat /proc/8909/cmdline
    bash
    
  2. Korzystanie ps -p PID --noheaders -o cmdjest tak samo bezużyteczne:

    $ ps -p 8909 --no-headers -o cmd
    bash
    
  3. ps -eaf nie jest również pomocne:

    $ 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
    

    Oznacza to, że nie ma danych wyjściowych z wiersza polecenia ORIGINAL, czego właśnie szukam - tj while true ; do echo 1 ; echo 2>/dev/null ; sleep 30 ; done.

Odpowiedzi:


40

Wiedziałem, że chwytam się słomek, ale UNIX nigdy nie zawodzi!

Oto jak to zrobiłem:

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

Następnie po (gdb)znaku zachęty uruchomiłem polecenie, call write_history("/tmp/foo")które zapisze tę historię do pliku /tmp/foo.

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

Następnie odłączam się od procesu.

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

I wyjdź gdb.

(gdb) q

I na pewno ...

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

Dla łatwego ponownego użycia napisałem skrypt bash , automatyzujący proces.


1
+1 bardzo fajne przemykanie. Pamiętaj, aby zaznaczyć to jako wyjątkowe A w ciągu 2 dni.
slm

@slm: Dzięki! Napiszę o tym post na blogu - polowanie na to było fajne.
ttsiodras

1
Z readline włączona można to zrobić także w gdb: print (char *)rl_line_buffer. Bieżące polecenie w sekwencji to print (char *)the_printed_command. Możesz również call history_builtin(), ale to wyświetli się w tty procesu bash, więc może być mniej przydatne.
mr.spuratic

11
@slm: Nie mogłem się oprzeć - blogowałem o tym tutaj: users.softlab.ece.ntua.gr/~ttsiod/bashheimer.html
ttsiodras

1
Czy bash jest bezpieczny dla wątków? Będziesz miał nadzieję, że nie zmienisz stanu wewnętrznego, który zakłóca aktualnie wykonywany kod podczas wykonywania czegoś z gdb. W praktyce bash prawie na pewno będzie czekał na zakończenie procesu potomnego, ilekroć zawiesisz go za pomocą gdb.
Adrian Pronk

5

Ponieważ polecenie nadal działa na ekranie, jego nadrzędny bash nie odczytał żadnej historii, więc:

  • podłącz ponownie do ekranu
  • naciśnij ^Zwtedyup arrow
  • bonus: zawiń polecenie w pojedyncze cudzysłowy (nawigacja za pomocą ^A^A- ponieważ screen (1) - i ^E) i echo + przekierowanie do pliku
  • fg w celu wykonania polecenia

Istnieją zastrzeżenia, ale jest to przydatne w większości przypadków.


Tak, nawet zabijanie i naciskanie up. Chociaż musisz mieć pewność, że uruchomi się ponownie bez żadnego problemu, ale jeśli nie, to i tak będziesz mieć ten sam problem po ponownym uruchomieniu. Musisz tylko wybrać moment, w którym przestój nie będzie problemem.
Matthieu Napoli

0

Wiem, że znalazłeś własną odpowiedź, ale jest powód, dla którego nie możesz zrobić czegoś takiego:

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

Być może nie można łączyć wyników rzeczywistego zadania z danymi wyjściowymi bash pokazującymi aktualnie wykonywaną linię.

Również FWIW, jeśli wolisz, gadatliwość, set -o xtrace.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.