使用命令行在进程启动后重定向 STDERR/STDOUT?

在 shell 中可以执行重定向、 > <等操作,但是在程序启动之后呢?

下面是我如何来问这个问题,一个程序运行在我的终端后台不断输出恼人的文本。这是一个重要的过程,所以我必须打开另一个 shell 来避免文本。我希望能够 >/dev/null或其他一些重定向,这样我就可以继续在同一个 shell 中工作。

89083 次浏览

除了关闭和重新打开 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 提供了几个虚拟 TTYs,您可以在这些虚拟 TTYs 之间进行切换,而无需打开新的 SSH/telnet/etc 会话
  • 使用 nohup; 这允许您关闭和重新打开会话,而不会在... 进程中丢失任何后台进程。

这并不是对您问题的直接回答,但是这是我在过去几天中发现的一个有用的技巧: 使用“ screen”运行初始命令,然后分离。

这样就行了:

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 一样。

将正在运行的进程的输出重定向到另一个终端、文件或屏幕:

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 命令的结果
  • 关闭标准输出
  • 要写入的终端
  • 关闭错误输出
  • 要写入的 /tmp/myerrlog文件
  • 退出 gdb
  • 在后台运行停止作业1
  • 从终端分离作业1
  • 停止正在运行的进程
  • [Ctrl+d]出口终端

抄袭弗拉德(和其他人)的杰出研究:

在同一目录中创建以下两个文件,路径中的某个文件,比如 $HOME/bin:

Gdb,包含(来自 vladr 的回答) :


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-警告 * * : 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。

这是 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
set scheduler-locking on
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

更新 : 对于 gdb v7.11及更高版本,需要使用 set scheduler-locking on或其他提到的 给你选项,因为在附加 gdb 之后,它不会停止所有正在运行的线程,而且由于文件使用,您可能无法关闭/打开日志文件。

Dupx 是一个简单的 * nix 实用程序,用于重定向已经运行的进程的标准输出/输入/错误。

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

可以使用 reredirect (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)。