Ответ узла действительно хорош, но он оставил открытым вопрос, как перенаправить stdout и stderr. Я нашел решение для Unix & Linux , но оно также не завершено. Я хотел бы объединить эти два решения. Вот оно:
Для своего теста я создал небольшой скрипт bash, называемый loop.sh, который печатает pid самого себя с минутным сном в бесконечном цикле.
$./loop.sh
Теперь как-нибудь получите PID этого процесса. Обычно ps -C loop.sh
достаточно хорошо, но в моем случае это напечатано.
Теперь мы можем переключиться на другой терминал (или нажать ^ Z и на том же терминале). Теперь gdb
должен быть присоединен к этому процессу.
$ gdb -p <PID>
Это останавливает скрипт (если запущен). Его состояние можно проверить с помощью ps -f <PID>
, где поле STAT
равно 'T +' (или в случае ^ Z 'T'), что означает (man ps (1))
T Stopped, either by a job control signal or because it is being traced
+ is in the foreground process group
(gdb) call close(1)
$1 = 0
Close (1) возвращает ноль в случае успеха.
(gdb) call open("loop.out", 01102, 0600)
$6 = 1
Open (1) возвращает новый дескриптор файла в случае успеха.
Это открытие равно open(path, O_TRUNC|O_CREAT|O_RDWR, S_IRUSR|S_IWUSR)
.
Вместо O_RDWR
O_WRONLY
можно применить, но /usr/sbin/lsof
говорит 'u' для всех обработчиков файлов std * (столбец FD
), что составляет O_RDWR
.
Я проверил значения в заголовочном файле /usr/include/bits/fcntl.h.
Выходной файл можно открыть с помощью O_APPEND
, как и nohup
, но это не рекомендуется man open(2)
из-за возможных проблем с NFS.
Если мы получим -1 в качестве возвращаемого значения, то call perror("")
напечатает сообщение об ошибке. Если нам нужна ошибка, используйте команду p errno
gdb.
Теперь мы можем проверить вновь перенаправленный файл. /usr/sbin/lsof -p <PID>
отпечатков:
loop.sh <PID> truey 1u REG 0,26 0 15008411 /home/truey/loop.out
Если мы хотим, мы можем перенаправить stderr в другой файл, если мы хотим снова использовать call close(2)
и call open(...)
, используя другое имя файла.
Теперь прикрепленный bash
должен быть освобожден, и мы можем выйти gdb
:
(gdb) detach
Detaching from program: /bin/bash, process <PID>
(gdb) q
Если скрипт был остановлен с помощью gdb
из другого терминала, он продолжает работать. Мы можем переключиться обратно на терминал loop.sh. Теперь он ничего не пишет на экран, но работает и пишет в файл. Мы должны поставить его на задний план. Итак, нажмите ^Z
.
^Z
[1]+ Stopped ./loop.sh
(Теперь мы находимся в том же состоянии, как если бы в начале была нажата ^Z
.)
Теперь мы можем проверить состояние задания:
$ ps -f 24522
UID PID PPID C STIME TTY STAT TIME CMD
<UID> <PID><PPID> 0 11:16 pts/36 S 0:00 /bin/bash ./loop.sh
$ jobs
[1]+ Stopped ./loop.sh
Таким образом, процесс должен работать в фоновом режиме и отключаться от терминала. Число в выводе команды jobs
в квадратных скобках обозначает задание внутри bash
. Мы можем использовать следующие встроенные команды bash
, применяющие знак «%» перед номером задания:
$ bg %1
[1]+ ./loop.sh &
$ disown -h %1
$ ps -f <PID>
UID PID PPID C STIME TTY STAT TIME CMD
<UID> <PID><PPID> 0 11:16 pts/36 S 0:00 /bin/bash ./loop.sh
И теперь мы можем выйти из вызывающего bash. Процесс продолжается в фоновом режиме. Если мы выйдем, его PPID станет 1 (процесс init (1)), а управляющий терминал станет неизвестным.
$ ps -f <PID>
UID PID PPID C STIME TTY STAT TIME CMD
<UID> <PID> 1 0 11:16 ? S 0:00 /bin/bash ./loop.sh
$ /usr/bin/lsof -p <PID>
...
loop.sh <PID> truey 0u CHR 136,36 38 /dev/pts/36 (deleted)
loop.sh <PID> truey 1u REG 0,26 1127 15008411 /home/truey/loop.out
loop.sh <PID> truey 2u CHR 136,36 38 /dev/pts/36 (deleted)
COMMENT
Материал gdb можно автоматизировать, создав файл (например, loop.gdb), содержащий команды, и запустите gdb -q -x loop.gdb -p <PID>
. Мой loop.gdb выглядит так:
call close(1)
call open("loop.out", 01102, 0600)
# call close(2)
# call open("loop.err", 01102, 0600)
detach
quit
Или вместо этого можно использовать следующий вкладыш:
gdb -q -ex 'call close(1)' -ex 'call open("loop.out", 01102, 0600)' -ex detach -ex quit -p <PID>
Надеюсь, это довольно полное описание решения.