Перенаправить STDERR / STDOUT процесса ПОСЛЕ его запуска с помощью командной строки?

127

В оболочке вы можете выполнять перенаправление > <и т. Д., Но как насчет ПОСЛЕ запуска программы?

Вот как я пришел к этому вопросу: программа, работающая в фоновом режиме моего терминала, продолжает выводить раздражающий текст. Это важный процесс, поэтому мне нужно открыть другую оболочку, чтобы избежать текста. Я бы хотел иметь возможность >/dev/nullили какое-то другое перенаправление, чтобы я мог продолжать работать в той же оболочке.

Ян Келлинг
источник
Я знаю, что самый простой способ перенаправить STDOUT / STDERR - это DUP2 их файловых дескрипторов ПЕРЕД разветвлением. Это довольно стандартная практика, и, вероятно, именно так оболочки делают это прямо сейчас. Не уверен, что это дает ответ, но я думаю, что это снижает шансы на то, что будет хороший ответ.
Стефан Май
1
reptyr
Луи

Ответы:

124

За исключением закрытия и повторного открытия вашего tty (т.е. выхода из системы и повторного включения, что также может привести к прекращению некоторых фоновых процессов в процессе), у вас остается только один выбор:

  • присоединитесь к рассматриваемому процессу с помощью gdb и запустите:
    • p dup2 (open ("/ dev / null", 0), 1)
    • p dup2 (open ("/ dev / null", 0), 2)
    • открепление
    • уволиться

например:

$ tail -f /var/log/lastlog &
[1] 5636

$ ls -l /proc/5636/fd
total 0
lrwx------ 1 myuser myuser 64 Feb 27 07:36 0 -> /dev/pts/0
lrwx------ 1 myuser myuser 64 Feb 27 07:36 1 -> /dev/pts/0
lrwx------ 1 myuser myuser 64 Feb 27 07:36 2 -> /dev/pts/0
lr-x------ 1 myuser myuser 64 Feb 27 07:36 3 -> /var/log/lastlog

$ gdb -p 5636
GNU gdb 6.8-debian
Copyright (C) 2008 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Attaching to process 5636
Reading symbols from /usr/bin/tail...(no debugging symbols found)...done.
Reading symbols from /lib/librt.so.1...(no debugging symbols found)...done.
Loaded symbols for /lib/librt.so.1
Reading symbols from /lib/libc.so.6...(no debugging symbols found)...done.
Loaded symbols for /lib/libc.so.6
Reading symbols from /lib/libpthread.so.0...(no debugging symbols found)...done.
[Thread debugging using libthread_db enabled]
[New Thread 0x7f3c8f5a66e0 (LWP 5636)]
Loaded symbols for /lib/libpthread.so.0
Reading symbols from /lib/ld-linux-x86-64.so.2...(no debugging symbols found)...done.
Loaded symbols for /lib64/ld-linux-x86-64.so.2

(no debugging symbols found)
0x00007f3c8eec7b50 in nanosleep () from /lib/libc.so.6

(gdb) p dup2(open("/dev/null",0),1)
[Switching to Thread 0x7f3c8f5a66e0 (LWP 5636)]
$1 = 1

(gdb) p dup2(open("/dev/null",0),2)
$2 = 2

(gdb) detach
Detaching from program: /usr/bin/tail, process 5636

(gdb) quit

$ ls -l /proc/5636/fd
total 0
lrwx------ 1 myuser myuser 64 Feb 27 07:36 0 -> /dev/pts/0
lrwx------ 1 myuser myuser 64 Feb 27 07:36 1 -> /dev/null
lrwx------ 1 myuser myuser 64 Feb 27 07:36 2 -> /dev/null
lr-x------ 1 myuser myuser 64 Feb 27 07:36 3 -> /var/log/lastlog
lr-x------ 1 myuser myuser 64 Feb 27 07:36 4 -> /dev/null
lr-x------ 1 myuser myuser 64 Feb 27 07:36 5 -> /dev/null

Вы также можете рассмотреть:

  • использование screen; экран предоставляет несколько виртуальных TTY, между которыми можно переключаться, не открывая новые сеансы SSH / telnet / и т. д.
  • использование nohup; это позволяет вам закрыть и снова открыть сеанс без потери каких-либо фоновых процессов в процессе ...
vladr
источник
1
Ваш ответ gdb не работал с файлом tail -f, и он не работал с тестовой программой на c, скомпилированной с помощью gcc -ggdb, которая выполняет printf каждую секунду. Также cont делает невозможным выполнение дополнительных команд gdb, команда будет отсоединена, а затем завершена.
Ян Келлинг,
Правильно насчет отсоединения, сейчас 2 часа ночи. :) Что именно не работало с решением gdb?
vladr
12
Если вы перенаправляете stdout / stderr (очевидно, на что-нибудь, кроме / dev / null), вам нужно открыть файл с доступом на запись - open("/path/to/new/stdout",O_WRONLY). Однако O_WRONLY, вероятно, будет недоступен; его значение находится 1в Linux / glibc.
Jander
13
Предупреждение: присоединение к процессу в gdb приостанавливает процесс до тех пор, пока вы не отключитесь от него.
Marty B
1
Добавление к комментарию @Jander, использование 1025активирует O_APPENDв дополнение к O_WRONLY, что удобно, если вы перенаправляете и stderr, и stdout в один и тот же файл.
спектры
57

Это будет делать:

strace -ewrite -p $PID

Это не так чисто (показывает строки вроде :) write(#,<text you want to see>), но работает!


Вам также может не понравиться тот факт, что аргументы сокращены. Для управления этим используйте -sпараметр, который устанавливает максимальную длину отображаемых строк.

Он улавливает все потоки, поэтому вы можете как-то отфильтровать это:

strace -ewrite -p $PID 2>&1 | grep "write(1" 

показывает только вызовы дескриптора 1. 2>&1заключается в перенаправлении STDERR в STDOUT, поскольку straceпо умолчанию выполняется запись в STDERR.

naugtur
источник
6
Это не то, о чем просила ОП. OP попросил ПЕРЕНАПРАВИТЬ от TTY, а не перехватывать. Кроме того, на некоторых платформах strace / truss будет вставлять пробелы между перехваченными символами потока и / или экранировать не-ASCII, и вам также придется иметь дело с их обработкой.
vladr 04
4
Да, это частично - но для некоторых людей, читающих этот вопрос, это все, что им нужно - увидеть, что происходит в программе, ошибочно запущенной для записи в null или на другой консоли. Я узнал это после того, как нашел этот вопрос в процессе, и подумал, что это хороший взлом (по крайней мере, для меня). И многие люди считают полезным, если мои глаза не верят мне;)
naugtur
Также возможно, что sudoэто необходимо.
colidyre
21

копируя прекрасные исследования Владра (и других):

создайте следующие два файла в одном каталоге, что-нибудь на вашем пути, скажем, $ HOME / bin:

silent.gdb, содержащий (из ответа владра):


p dup2(open("/dev/null",0),1)
p dup2(open("/dev/null",0),2)
detach
quit

и тишина, содержащая:


#!/bin/sh
if [ "$0" -a "$1" ]; then
 gdb -p $1 -x $0.gdb
else
 echo Must specify PID of process to silence >&2
fi

chmod +x ~/bin/silence  # make the script executable

Теперь, когда вы в следующий раз забудете перенаправить, например, firefox, и ваш терминал начнет загромождаться неизбежными сообщениями "(firefox-bin: 5117): Gdk-WARNING **: XID collision, проблема впереди":


ps  # look for process xulrunner-stub (in this case we saw the PID in the error above)
silence 5117  # run the script, using PID we found

Вы также можете перенаправить вывод gdb в / dev / null, если не хотите его видеть.

jcomeau_ictx
источник
3
В моем gdb (v7.2) есть удобная опция, --batch-silentкоторая подавляет вывод и не выгружает вас в консоль gdb, если что-то пойдет не так (например, отсутствует процесс). Кстати, $!относится к самому последнему фоновому заданию, но я не думаю, что его можно использовать в самом скрипте. Я использую псевдоним: alias silencebg='silence $!'
seanf
18

Перенаправить вывод запущенного процесса на другой терминал, файл или экран:

tty
ls -l /proc/20818/fd
gdb -p 20818

Внутри GDB :

p close(1)
p open("/dev/pts/4", 1)
p close(2)
p open("/tmp/myerrlog", 1)
q

Отсоедините запущенный процесс от терминала bash и оставьте его в живых:

[Ctrl+z]
bg %1 && disown %1
[Ctrl+d]

Объяснение:

20818 - просто пример запущенного процесса pid
p - распечатать результат команды gdb
close (1) - закрыть стандартный вывод
/ dev / pts / 4 - терминал для записи, чтобы
закрыть (2) - закрыть вывод ошибки
/ tmp / myerrlog - файл в запись в
q - выйти из gdb
bg% 1 - запустить остановленное задание 1 в фоновом режиме
disown% 1 - отсоединить задание 1 от терминала

Мирек
источник
2
Это не сработает, если stdin(дескриптор файла 0) закрыт.
pabouk
Это спасло мне день. У меня был рабочий make в ssl-сеансе, который занимал час для первых 10%, и я действительно не хотел, чтобы мой ноутбук работал еще 10 часов. Но правильно ли я предполагаю, что ваше перенаправление для stderr должно читаться p open("/tmp/myerrlog", 2)?
GerardV
Была очень небольшая проблема с запуском на CentOS 6 - файл «/ tmp / myerrlog» должен был уже существовать. Конечно, создать его на ощупь было тривиально.
ebneter 06
3

Это не прямой ответ на ваш вопрос, но в последние несколько дней я нашел полезной эту технику: выполнить начальную команду с помощью «screen», а затем отсоединиться.

Роджер Липскомб
источник
2

это часть сценария bash, основанная на предыдущих ответах, которые перенаправляют файл журнала во время выполнения открытого процесса, он используется как постскрипт в logrotateпроцессе

#!/bin/bash

pid=$(cat /var/run/app/app.pid)
logFile="/var/log/app.log"

reloadLog()
{
    if [ "$pid" = "" ]; then
        echo "invalid PID"
    else
        gdb -p $pid >/dev/null 2>&1 <<LOADLOG
p close(1)
p open("$logFile", 1)
p close(2)
p open("$logFile", 1)
q
LOADLOG
        LOG_FILE=$(ls /proc/${pid}/fd -l | fgrep " 1 -> " | awk '{print $11}')
        echo "log file set to $LOG_FILE"
    fi
}

reloadLog
Мостафа Назари
источник
1

Dupx - это простая утилита * nix для перенаправления стандартного вывода / ввода / ошибки уже запущенного процесса.

https://www.isi.edu/~yuri/dupx/

eMPee584
источник
0

Вы можете использовать переадресацию ( https://github.com/jerome-pouiller/reredirect/ ).

Тип

reredirect -m FILE PID

а выходные данные (стандартные и ошибочные) будут записаны в ФАЙЛ.

reredirect README также объясняет, как восстановить исходное состояние процесса, как перенаправить на другую команду или перенаправить только stdout или stderr.

reredirectтакже предоставьте скрипт, relinkкоторый позволяет перенаправить на текущий терминал:

relink PID
relink PID | grep usefull_content

(reredirect, похоже, имеет те же функции, что и Dupx, описанный в другом ответе, но это не зависит от Gdb).

Жером Пуйлер
источник