เปลี่ยนเส้นทาง STDERR / STDOUT ของกระบวนการหลังจากเริ่มต้นแล้วโดยใช้บรรทัดคำสั่ง?


127

ในเชลล์คุณสามารถเปลี่ยนเส้นทางได้> <ฯลฯ แต่หลังจากเริ่มโปรแกรมแล้วจะเป็นอย่างไร?

นี่คือวิธีที่ฉันถามคำถามนี้โปรแกรมที่ทำงานอยู่เบื้องหลังเทอร์มินัลของฉันจะส่งข้อความที่น่ารำคาญออกมา เป็นกระบวนการที่สำคัญดังนั้นฉันจึงต้องเปิดเชลล์อื่นเพื่อหลีกเลี่ยงข้อความ ฉันต้องการที่จะสามารถ>/dev/nullหรือเปลี่ยนเส้นทางอื่น ๆ เพื่อที่ฉันจะได้ทำงานในเชลล์เดียวกันต่อไป


ฉันรู้วิธีที่ง่ายที่สุดในการเปลี่ยนเส้นทาง STDOUT / STDERR คือ DUP2 ตัวอธิบายไฟล์ของพวกเขาก่อนที่จะทำการฟอร์ก นี่เป็นแนวทางปฏิบัติที่ค่อนข้างเป็นมาตรฐานและอาจเป็นวิธีที่เชลล์ทำได้ในตอนนี้ ไม่แน่ใจว่าจะให้คำตอบหรือเปล่า แต่ฉันคิดว่ามันช่วยลดโอกาสในการมีคำตอบที่ดี
Stefan Mai

คำตอบ:


124

ขาดการปิดและเปิด tty ของคุณอีกครั้ง (เช่นการออกจากระบบและเปิดใหม่ซึ่งอาจทำให้กระบวนการพื้นหลังบางส่วนของคุณสิ้นสุดลงในกระบวนการ) คุณมีทางเลือกเพียงทางเดียวเท่านั้น:

  • แนบกับกระบวนการที่เป็นปัญหาโดยใช้ gdb และเรียกใช้:
    • p dup2 (เปิด ("/ dev / null", 0), 1)
    • p dup2 (เปิด ("/ dev / null", 0), 2)
    • detach
    • เลิก

เช่น:

$ 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 / etc ใหม่
  • ใช้nohup; สิ่งนี้ช่วยให้คุณสามารถปิดและเปิดเซสชันของคุณใหม่ได้โดยไม่สูญเสียกระบวนการเบื้องหลังในกระบวนการ ...

1
คำตอบ gdb ของคุณใช้ไม่ได้กับไฟล์ tail -f และใช้ไม่ได้กับโปรแกรมทดสอบใน c ที่คอมไพล์ด้วย gcc -ggdb ที่ทำ printf ทุกวินาที นอกจากนี้ cont ทำให้ไม่สามารถเรียกใช้คำสั่ง gdb ได้อีกคำสั่งจะถูกแยกออกจากนั้นจึงออก
Ian Kelling

ถูกต้องเกี่ยวกับการถอดมันคือ 2AM :) อะไรที่ใช้ไม่ได้กับโซลูชัน gdb?
vladr

12
หากคุณกำลังเปลี่ยนทิศ / 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 ตามค่าเริ่มต้น


6
นี่ไม่ใช่สิ่งที่ OP ขอ OP ขอให้เปลี่ยนเส้นทางออกจาก TTY ไม่ใช่สกัดกั้น นอกจากนี้ในบางแพลตฟอร์ม strace / truss จะแทรกช่องว่างระหว่างอักขระสตรีมที่ถูกสกัดกั้นและ / หรือหลีกเลี่ยงที่ไม่ใช่ ASCII และคุณจะต้องจัดการกับการประมวลผลเหล่านั้นด้วย
vladr

4
ใช่สิ่งนี้ทำบางส่วน - แต่สำหรับบางคนที่อ่านคำถามนี้สิ่งที่พวกเขาต้องการคือทั้งหมดที่พวกเขาต้องการเพื่อดูว่าเกิดอะไรขึ้นในโปรแกรมที่ทำงานผิดพลาดเพื่อเขียนเป็น null หรือบนคอนโซลอื่น ฉันพบมันหลังจากพบคำถามนี้ในกระบวนการและคิดว่ามันเป็นการแฮ็กที่ดี (อย่างน้อยสำหรับฉัน) และมีคนไม่กี่คนที่คิดว่ามันมีประโยชน์ถ้าตาของฉันไม่เชื่อฉัน;)
naugtur

นอกจากนี้ยังเป็นไปได้ที่sudoจำเป็น
colidyre

21

ปิดการวิจัยที่ยอดเยี่ยมของวลาด (และอื่น ๆ ):

สร้างสองไฟล์ต่อไปนี้ในไดเร็กทอรีเดียวกันบางอย่างในเส้นทางของคุณพูด $ HOME / bin:

silence.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 ปัญหาข้างหน้า":


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 ได้หากคุณไม่ต้องการเห็น


3
gdb ของฉัน (v7.2) มีตัวเลือก--batch-silentที่ใช้งานง่ายซึ่งจะระงับเอาต์พุตและไม่ทิ้งคุณลงในคอนโซล gdb หากมีสิ่งผิดปกติเกิดขึ้น (เช่นกระบวนการที่ขาดหายไป) BTW $!หมายถึงงานเบื้องหลังล่าสุด แต่ฉันไม่คิดว่าจะสามารถใช้ในสคริปต์ได้ ฉันใช้นามแฝง: 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 terminal และทำให้มันมีชีวิตอยู่:

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

คำอธิบาย:

20818 - เพียงตัวอย่างของการรัน process pid
p - ผลลัพธ์การพิมพ์ของคำสั่ง gdb
close (1) - ปิดเอาต์พุตมาตรฐาน
/ dev / pts / 4 - เทอร์มินัลที่จะเขียนเพื่อ
ปิด (2) - ปิดเอาต์พุตข้อผิดพลาด
/ tmp / myerrlog - ไฟล์ไปที่ เขียนถึง
q - ออกจาก gdb
bg% 1 - รันงานที่หยุด 1 บนพื้นหลัง
ปฏิเสธ% 1 - ปลดงาน 1 ออกจากเทอร์มินัล


2
สิ่งนี้จะไม่ทำงานหากปิดstdin(file descriptor 0)
pabouk

วันนี้ช่วยฉันไว้ ฉันทำงานในเซสชัน ssl โดยใช้เวลาหนึ่งชั่วโมงสำหรับ 10% แรกและฉันไม่ต้องการให้แล็ปท็อปทำงานต่อไปอีก 10 ชั่วโมง แต่ฉันคิดถูกp open("/tmp/myerrlog", 2)หรือไม่ที่จะถือว่าการเปลี่ยนเส้นทางของคุณสำหรับ stderr ควรอ่าน
GerardV

มีปัญหาเล็กน้อยเกี่ยวกับสิ่งนี้ที่ทำงานบน CentOS 6 - ไฟล์ "/ tmp / myerrlog" ต้องมีอยู่แล้ว แน่นอนว่ามันเป็นเรื่องเล็กน้อยที่จะสร้างมันด้วยการสัมผัส
ebneter

3

ไม่ใช่คำตอบโดยตรงสำหรับคำถามของคุณ แต่เป็นเทคนิคที่ฉันพบว่ามีประโยชน์ในช่วงสองสามวันที่ผ่านมา: เรียกใช้คำสั่งเริ่มต้นโดยใช้ "หน้าจอ" จากนั้นแยกออก


2

นี่คือส่วนของสคริปต์ทุบตีตามคำตอบก่อนหน้าซึ่งเปลี่ยนเส้นทางไฟล์บันทึกในระหว่างการดำเนินการของกระบวนการเปิดซึ่งจะใช้เป็นคำลงท้ายใน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/


0

คุณสามารถใช้การเปลี่ยนเส้นทางซ้ำ ( https://github.com/jerome-pouiller/reredirect/ )

ชนิด

reredirect -m FILE PID

และเอาต์พุต (มาตรฐานและข้อผิดพลาด) จะถูกเขียนใน FILE

การเปลี่ยนเส้นทาง README ยังอธิบายถึงวิธีการคืนค่าสถานะดั้งเดิมของกระบวนการวิธีเปลี่ยนเส้นทางไปยังคำสั่งอื่นหรือเปลี่ยนเส้นทางเฉพาะ stdout หรือ stderr

reredirectยังมีสคริปต์ที่เรียกrelinkว่าอนุญาตให้เปลี่ยนเส้นทางไปยังเทอร์มินัลปัจจุบัน:

relink PID
relink PID | grep usefull_content

(การเปลี่ยนเส้นทางดูเหมือนจะมีคุณสมบัติเหมือนกันกับ Dupx ที่อธิบายไว้ในคำตอบอื่น แต่ไม่ได้ขึ้นอยู่กับ Gdb)

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.