Przekierować STDERR / STDOUT procesu PO jego uruchomieniu, używając wiersza poleceń?


129

W powłoce można wykonać przekierowanie > <itp., Ale co powiesz PO uruchomieniu programu?

Oto, jak przyszedłem zadać to pytanie: program działający w tle mojego terminala ciągle wyświetla irytujący tekst. To ważny proces, więc muszę otworzyć inną powłokę, aby uniknąć tekstu. Chciałbym mieć możliwość >/dev/nulllub inne przekierowanie, abym mógł dalej pracować w tej samej powłoce.


Wiem, że najłatwiejszym sposobem przekierowania STDOUT / STDERR jest DUP2 ich deskryptorów plików PRZED rozwidleniem. Jest to dość standardowa praktyka i prawdopodobnie sposób, w jaki robią to teraz pociski. Nie jestem pewien, czy to daje odpowiedź, ale myślę, że zmniejsza to szanse na dobrą.
Stefan Mai

Odpowiedzi:


126

Oprócz zamykania i ponownego otwierania terminala (tj. Wylogowania się i ponownego zalogowania, co może również zakończyć niektóre procesy w tle), pozostaje tylko jedna możliwość:

  • dołącz do danego procesu za pomocą gdb i uruchom:
    • p dup2 (open ("/ dev / null", 0), 1)
    • p dup2 (open ("/ dev / null", 0), 2)
    • odłączyć
    • porzucić

na przykład:

$ 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

Możesz również rozważyć:

  • za pomocą screen; screen udostępnia kilka wirtualnych TTY, między którymi można się przełączać bez konieczności otwierania nowych sesji SSH / telnet / etc
  • za pomocą nohup; umożliwia to zamknięcie i ponowne otwarcie sesji bez utraty procesów w tle w procesie ...

1
Twoja odpowiedź gdb nie działała z plikiem tail -f i nie działała z programem testowym w c skompilowanym za pomocą gcc -ggdb, który wykonuje printf co sekundę. Cont również uniemożliwia uruchomienie większej liczby poleceń gdb, polecenie to odłącza się, a następnie kończy.
Ian Kelling

Prawidłowo odłączyć, jest 2 w nocy. :) Co dokładnie nie zadziałało z rozwiązaniem gdb?
vladr

12
Jeśli przekierowujesz stdout / stderr (najwyraźniej do czegokolwiek innego niż / dev / null), musisz otworzyć plik z prawem do zapisu - open("/path/to/new/stdout",O_WRONLY). O_WRONLY prawdopodobnie nie będzie jednak dostępny; jego wartość znajduje się 1w systemie Linux / glibc.
Jander

13
Jedna uwaga: dołączenie do procesu w gdb wstrzymuje proces do momentu odłączenia się od niego.
Marty B

1
Dodanie do komentarza @Jandera, użycie dodatkowo 1025aktywuje , co jest przydatne, jeśli przekierowujesz zarówno stderr, jak i stdout do tego samego pliku. O_APPENDO_WRONLY
spectras

57

To wystarczy:

strace -ewrite -p $PID

Nie jest tak czysty (pokazuje linie takie jak:) write(#,<text you want to see>), ale działa!


Może Ci się również nie spodobać fakt, że argumenty są skrócone. Aby kontrolować, użyj -sparametru, który ustawia maksymalną długość wyświetlanych ciągów.

Przechwytuje wszystkie strumienie, więc możesz chcieć to jakoś przefiltrować:

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

pokazuje tylko wywołania deskryptora 1. 2>&1jest przekierowanie STDERR do STDOUT, ponieważ stracedomyślnie zapisuje do STDERR.


6
Nie o to prosił PO. OP poprosił o PRZEKIEROWANIE z dala od TTY, a nie przechwytywanie. Ponadto na niektórych platformach strace / truss wstawi spacje między przechwyconymi znakami strumienia i / lub ucieknie spoza ASCII i będziesz musiał poradzić sobie z ich przetwarzaniem.
vladr

4
Tak, to częściowo rozwiązuje problem - ale dla niektórych osób czytających to pytanie to wszystko, czego potrzebują - aby zobaczyć, co dzieje się w programie, który przez pomyłkę uruchamia zapis do wartości null lub na innej konsoli. Dowiedziałem się tego po znalezieniu tego pytania i pomyślałem, że to niezły hack (przynajmniej dla mnie). I sporo osób uważa, że ​​pomocne, jeśli moje oczy mnie nie
zwodzą

Możliwe, że sudojest to potrzebne.
colidyre

21

nabijając się na doskonałych badaniach vladr (i innych):

utwórz następujące dwa pliki w tym samym katalogu, coś w swojej ścieżce, powiedz $ HOME / bin:

silence.gdb, zawierający (z odpowiedzi vladr):


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

i cisza zawierająca:


#!/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

Teraz następnym razem, gdy zapomnisz przekierować na przykład Firefoksa, a Twój terminal zacznie się zaśmiecać komunikatami nieuniknionymi „(firefox-bin: 5117): Gdk-WARNING **: kolizja XID, problemy z wyprzedzeniem”:


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

Możesz również przekierować wyjście gdb do / dev / null, jeśli nie chcesz go widzieć.


3
Mój gdb (v7.2) ma przydatną opcję, --batch-silentktóra blokuje wyjście i nie zrzuca cię do konsoli gdb, jeśli coś pójdzie nie tak (np. Brak procesu). BTW, $!odnosi się do ostatniego zadania w tle, ale nie sądzę, aby można go było użyć w samym skrypcie. Używam aliasu: alias silencebg='silence $!'
seanf

19

Przekieruj dane wyjściowe z uruchomionego procesu do innego terminala, pliku lub ekranu:

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

Wewnątrz gdb :

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

Odłącz działający proces od terminala bash i utrzymuj go przy życiu:

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

Wyjaśnienie:

20818 - przykład uruchomionego procesu pid
p - wypisuje wynik polecenia gdb
close (1) - zamknij standardowe wyjście
/ dev / pts / 4 - terminal do zapisu do
zamknięcia (2) - zamknij wyjście błędu
/ tmp / myerrlog - plik do zapisz do
q - wyjdź z gdb
bg% 1 - uruchom zatrzymane zadanie 1 w tle
wykluczone% 1 - odłącz zadanie 1 od terminala


2
To nie zadziała, jeśli stdin(deskryptor pliku 0) jest zamknięty.
pabuk

To uratowało mi dzień. Miałem uruchomioną markę w sesji ssl, która zajęła godzinę dla pierwszych 10% i naprawdę nie chciałem, aby mój laptop działał przez kolejne 10 godzin. Ale czy mam rację, zakładając, że twoje przekierowanie na stderr powinno przeczytać p open("/tmp/myerrlog", 2)?
GerardV

Wystąpił bardzo drobny problem z tym działaniem w CentOS 6 - plik „/ tmp / myerrlog” musiał już istnieć. Oczywiście tworzenie go dotykiem było trywialne.
ebneter

3

Nie jest to bezpośrednia odpowiedź na twoje pytanie, ale jest to technika, którą uważam za przydatną przez ostatnie kilka dni: uruchom początkowe polecenie za pomocą „screen”, a następnie odłącz.


3

jest to część skryptu basha oparta na poprzednich odpowiedziach, która przekierowuje plik dziennika podczas wykonywania otwartego procesu, jest używany jako postscript w logrotateprocesie

#!/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


0

Możesz użyć reredirect ( https://github.com/jerome-pouiller/reredirect/ ).

Rodzaj

reredirect -m FILE PID

a wyjścia (standardowe i błędne) zostaną zapisane w PLIKU.

reredirect README wyjaśnia również, jak przywrócić pierwotny stan procesu, jak przekierować do innej komendy lub przekierować tylko stdout lub stderr.

reredirectdostarcza również skrypt o nazwie, relinkktóry umożliwia przekierowanie do bieżącego terminala:

relink PID
relink PID | grep usefull_content

(wydaje się, że reredirect ma te same cechy, co Dupx opisany w innej odpowiedzi, ale nie zależy od Gdb).

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.