Redirect STDERR / STDOUT dari suatu proses SETELAH itu sudah dimulai, menggunakan baris perintah?

127

Dalam shell Anda dapat melakukan pengalihan > <,, dll., Tetapi bagaimana SETELAH program dimulai?

Inilah cara saya datang untuk menanyakan pertanyaan ini, sebuah program yang berjalan di latar belakang terminal saya terus mengeluarkan teks yang mengganggu. Ini adalah proses yang penting jadi saya harus membuka shell lain untuk menghindari teks. Saya ingin dapat melakukan >/dev/nullatau pengalihan lainnya sehingga saya dapat terus bekerja di shell yang sama.

Ian Kelling
sumber
Saya tahu cara termudah untuk mengarahkan kembali STDOUT / STDERR adalah dengan DUP2 deskriptor file mereka SEBELUM forking. Ini adalah praktik yang cukup standar, dan mungkin cara shell menyelesaikannya sekarang. Tidak yakin apakah itu memberikan jawaban, tapi saya pikir itu mengurangi kemungkinan ada yang bagus.
Stefan Mai
1
reptyr
Louis

Jawaban:

124

Pendek menutup dan membuka kembali tty Anda (yaitu log off dan kembali, yang juga dapat menghentikan beberapa proses latar belakang Anda dalam proses) Anda hanya memiliki satu pilihan tersisa:

  • lampirkan ke proses yang dimaksud menggunakan gdb, dan jalankan:
    • p dup2 (terbuka ("/ dev / null", 0), 1)
    • p dup2 (terbuka ("/ dev / null", 0), 2)
    • melepaskan
    • berhenti

misalnya:

$ 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

Anda juga dapat mempertimbangkan:

  • menggunakan screen; Layar menyediakan beberapa TTY virtual yang dapat Anda alihkan tanpa harus membuka sesi SSH / telnet / etc baru
  • menggunakan nohup; ini memungkinkan Anda untuk menutup dan membuka kembali sesi Anda tanpa kehilangan proses latar belakang dalam ... proses.
vladr
sumber
1
Jawaban gdb Anda tidak bekerja dengan file tail -f, dan itu tidak bekerja dengan program uji di c dikompilasi dengan gcc -ggdb yang melakukan printf setiap detik. Juga cont membuat tidak mungkin untuk menjalankan lebih banyak perintah gdb, perintah akan dilepaskan, kemudian berhenti.
Ian Kelling
Benar tentang detach, ini 2AM. :) Apa sebenarnya yang tidak berfungsi dengan solusi gdb?
vladr
12
Jika Anda mengarahkan ulang stdout / stderr (ke selain selain / dev / null), Anda perlu membuka file dengan akses tulis - open("/path/to/new/stdout",O_WRONLY). O_WRONLY mungkin tidak akan tersedia; nilainya 1di Linux / glibc.
Jander
13
Sebuah kata hati-hati: melampirkan ke suatu proses di gdb menjeda proses sampai Anda melepaskannya.
Marty B
1
Menambahkan ke komentar @Jander, menggunakan 1025aktifkan O_APPENDdi samping O_WRONLY, yang berguna jika Anda mengarahkan stderr dan stdout ke file yang sama.
spektrum
57

Ini akan melakukan:

strace -ewrite -p $PID

Ini tidak begitu bersih (menunjukkan garis-garis seperti write(#,<text you want to see>):), tetapi bekerja!


Anda mungkin juga tidak menyukai fakta bahwa argumen disingkat. Untuk mengontrol yang menggunakan -sparameter yang menetapkan panjang maksimum string yang ditampilkan.

Itu menangkap semua aliran, jadi Anda mungkin ingin memfilter itu entah bagaimana:

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

hanya menampilkan panggilan deskriptor 1. 2>&1adalah mengarahkan STDERR ke STDOUT, seperti stracemenulis ke STDERR secara default.

naugtur
sumber
6
Ini bukan yang diminta OP. OP diminta untuk MENGURANGI menjauh dari TTY, tidak memotong. Selain itu, pada beberapa platform strace / truss akan menyisipkan spasi antara karakter stream yang dicegat dan / atau keluar dari non-ASCII, dan Anda harus berurusan dengan pemrosesan juga.
vladr
4
Ya, ini melakukan sebagian - tetapi bagi sebagian orang yang membaca pertanyaan ini hanya itu yang mereka butuhkan - untuk melihat apa yang terjadi dalam program yang dijalankan dengan keliru untuk menulis ke nol atau pada konsol lain. Saya menemukannya setelah menemukan pertanyaan ini dalam proses dan berpikir itu adalah hack yang bagus (setidaknya untuk saya). DAN beberapa orang merasa
terbantu
Mungkin juga itu sudodiperlukan.
colidyre
21

riffing off vladr (dan lainnya ') penelitian yang sangat baik:

buat dua file berikut di direktori yang sama, sesuatu di path Anda, katakanlah $ HOME / bin:

silence.gdb, mengandung (dari jawaban vladr):


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

dan diam, mengandung:


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

Sekarang, lain kali Anda lupa untuk mengarahkan ulang firefox, misalnya, dan terminal Anda mulai berantakan dengan pesan yang tak terhindarkan "(firefox-bin: 5117): PERINGATAN Gdk **: tabrakan XID, masalah di depan":


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

Anda juga dapat mengarahkan output gdb ke / dev / null jika Anda tidak ingin melihatnya.

jcomeau_ictx
sumber
3
Gdb saya (v7.2) memiliki opsi praktis --batch-silentyang menekan output dan tidak membuang Anda ke konsol gdb jika terjadi kesalahan (mis. Proses yang hilang). BTW, $!mengacu pada pekerjaan latar belakang terbaru, tapi saya tidak berpikir itu bisa digunakan dalam skrip itu sendiri. Saya menggunakan alias: alias silencebg='silence $!'
seanf
18

Redirect output dari proses yang sedang berjalan ke terminal, file atau layar lain:

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

Di dalam gdb :

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

Lepaskan proses yang berjalan dari terminal bash dan biarkan tetap hidup:

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

Penjelasan:

20818 - hanya contoh dari proses yang berjalan pid
p - cetak hasil dari perintah gdb
tutup (1) - tutup keluaran standar
/ dev / pts / 4 - terminal untuk menulis untuk
ditutup (2) - tutup keluaran kesalahan
/ tmp / myerrlog - file to tulis ke
q - berhenti gdb
bg% 1 - jalankan pekerjaan yang dihentikan 1 pada latar belakang yang
ditolak% 1 - lepas kerja 1 dari terminal

Mirek
sumber
2
Ini tidak akan berfungsi jika stdin(deskriptor file 0) ditutup.
pabouk
Ini menyelamatkan hari saya. Saya memiliki performa berlari dalam sesi ssl yang menghabiskan waktu satu jam untuk 10% pertama dan saya benar-benar tidak ingin laptop saya berjalan selama 10 jam lagi. Tetapi apakah saya benar untuk menganggap pengalihan Anda untuk stderr harus dibaca p open("/tmp/myerrlog", 2)?
GerardV
Punya masalah yang sangat kecil dengan ini berjalan pada CentOS 6 - file "/ tmp / myerrlog" harus sudah ada. Itu sepele untuk membuatnya dengan sentuhan, tentu saja.
ebneter
3

Bukan jawaban langsung untuk pertanyaan Anda, tetapi ini adalah teknik yang menurut saya bermanfaat selama beberapa hari terakhir: Jalankan perintah awal menggunakan 'layar', lalu lepaskan.

Roger Lipscombe
sumber
2

ini adalah bagian skrip bash berdasarkan jawaban sebelumnya, yang mengarahkan file log selama eksekusi proses terbuka, digunakan sebagai postscript dalam logrotateproses

#!/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
Mostafa Nazari
sumber
0

Anda dapat menggunakan reredirect ( https://github.com/jerome-pouiller/reredirect/ ).

Tipe

reredirect -m FILE PID

dan output (standar dan kesalahan) akan ditulis dalam FILE.

reredirect README juga menjelaskan cara mengembalikan keadaan proses semula, cara mengarahkan ulang ke perintah lain atau hanya mengarahkan stdout atau stderr.

reredirectjuga menyediakan skrip bernama relinkyang memungkinkan untuk mengarahkan ulang ke terminal saat ini:

relink PID
relink PID | grep usefull_content

(reredirect tampaknya memiliki fitur yang sama dari Dupx yang dijelaskan dalam jawaban lain tetapi, itu tidak tergantung pada Gdb).

Jérôme Pouiller
sumber