Как я могу увидеть точную командную строку, выполняемую внутри некоторого экземпляра bash?

29

У меня есть долго работающий bashэкземпляр (внутри screenсеанса), который выполняет сложный набор команд внутри цикла (каждый цикл выполняет конвейеры, перенаправления и т. Д.).

Длинная командная строка была написана внутри терминала - она ​​не внутри скрипта. Теперь я знаю идентификатор процесса bash, и у меня есть root-доступ - как я могу увидеть точную командную строку, выполняемую внутри этого bash?

пример
bash$ echo $$
1234
bash$ while true ; do \
    someThing | somethingElse 2>/foo/bar | \
    yetAnother ; sleep 600 ; done

И в другом экземпляре оболочки я хочу видеть командную строку, выполняемую внутри 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'

Это возможно?

РЕДАКТИРОВАТЬ # 1

Добавление контрпримеров для некоторых ответов, которые я получил.

  1. Об использовании cmdlineunder /proc/PID: это не работает, по крайней мере, в моем сценарии. Вот простой пример:

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

    В другой оболочке:

    $ cat /proc/8909/cmdline
    bash
    
  2. Использование ps -p PID --noheaders -o cmdтак же бесполезно:

    $ ps -p 8909 --no-headers -o cmd
    bash
    
  3. ps -eaf Также не полезно:

    $ 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
    

    То есть нет выхода ОРИГИНАЛА командной строки, которая является то , что я ищу - то есть while true ; do echo 1 ; echo 2>/dev/null ; sleep 30 ; done.

ttsiodras
источник

Ответы:

40

Я знал, что цепляюсь за соломинку, но UNIX никогда не выходит из строя!

Вот как мне это удалось:

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

Затем в (gdb)командной строке я запустил команду, call write_history("/tmp/foo")которая запишет эту историю в файл /tmp/foo.

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

Я тогда отсоединяюсь от процесса.

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

И уйти gdb.

(gdb) q

И конечно же ...

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

Для легкого повторного использования в будущем я написал скрипт bash , автоматизирующий процесс.

ttsiodras
источник
1
+1 очень хорошая слежка. Обязательно отметьте это как исключенное за 2 дня.
СЛМ
@slm: Спасибо! Я напишу в блоге об этом - это было весело, охотиться за этим.
ttsiodras
1
С Readline включен , то вы также можете сделать это gdb: print (char *)rl_line_buffer. Текущая команда в последовательности print (char *)the_printed_command. Вы также можете call history_builtin(), но это будет выводить tty процесса bash, так что это может быть менее полезным.
mr.spuratic
11
@slm: Не смог устоять - я написал об этом здесь: users.softlab.ece.ntua.gr/~ttsiod/bashheimer.html
ttsiodras
1
Является ли Bash потокобезопасным? Вы должны будете надеяться, что вы не изменяете какое-то внутреннее состояние, которое нарушает текущий исполняемый код, когда вы выполняете что-то из gdb. На практике bash почти наверняка будет ожидать завершения дочернего процесса всякий раз, когда вам случится приостановить его с помощью gdb.
Адриан Пронк
5

Поскольку команда все еще выполняется на экране, ее родительский bash не перечитал историю, поэтому:

  • подключить к экрану
  • нажмите ^Zтогдаup arrow
  • бонус: заключить команду в одинарные кавычки (навигация с помощью ^A^A- потому что screen (1) - и ^E) и echo + перенаправление в файл
  • fg продолжать выполнение команды

Есть предостережения, но в большинстве случаев это достаточно полезно.

Lloeki
источник
Да, даже убить его и нажать up. Хотя вы должны быть уверены, что он запустится снова без проблем, но если этого не произойдет, у вас все равно будет такая же проблема после перезагрузки. Вам просто нужно выбрать момент, когда простоя не будет проблемой.
Матье Наполи
0

Я знаю, что вы нашли свой собственный ответ, но есть ли причина, по которой вы не можете сделать что-то вроде этого:

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

Возможно, вы не можете смешать вывод фактического задания и вывод bash, показывая текущую выполняемую строку.

Кроме того , FWIW, если вы предпочитаете многословие, set -o xtrace.

малиново-цапля
источник