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


127

在外壳中,您可以执行重定向> <等操作,但是启动程序后如何处理?

这就是我问这个问题的方式,在我的终端后台运行的程序不断输出令人讨厌的文本。这是一个重要的过程,因此我必须打开另一个shell以避免输入文本。我希望能够进行>/dev/null重定向或其他重定向,以便可以在同一外壳中继续工作。


我知道重定向STDOUT / STDERR的最简单方法是在分叉之前将其文件描述符DUP2。这是一个相当标准的做法,可能是shell现在完成它的方式。不确定是否能给出答案,但我认为这会减少获得一个好的答案的机会。
Stefan Mai,

Answers:


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 / etc,会话的情况下进行切换
  • 使用nohup; 这样,您就可以关闭并重新打开会话,而不会丢失...进程中的任何后台进程。

1
您的gdb答案不适用于tail -f文件,并且不适用于使用gcc -ggdb编译的c每秒执行一次printf的c中的测试程序。同样,由于无法继续运行更多的gdb命令,该命令将被分离,然后退出。
伊恩·凯灵

正确的分离,这是凌晨2点。:)究竟什么与gdb解决方案不兼容?
vladr

12
如果您要将stdout / stderr重定向(显然是/ dev / null以外的任何其他文件),则需要使用写入权限-打开文件open("/path/to/new/stdout",O_WRONLY)。但是O_WRONLY可能不可用;它的值1在Linux / glibc上。
詹德,2010年

13
请注意:在gdb中附加到进程会暂停该进程,直到您将其分离。
Marty B

1
添加到@Jander的评论,使用1025激活O_APPEND除了O_WRONLY,如果你既重定向标准错误和标准输出到同一个文件,该文件是很方便的。
光谱

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要求将REDIRECT远离TTY,而不是拦截。此外,在某些平台上,strace / truss会在截取的流字符之间插入空格和/或转义非ASCII,并且您也必须处理这些字符。
vladr

4
是的,这部分完成了任务-但对于某些阅读此问题的人来说,这正是他们所需要的-看看错误地运行以写入null或在另一个控制台上写入的程序中发生了什么。在过程中找到这个问题后,我发现了这个问题,并认为这是一个不错的技巧(至少对我而言)。而且很多人发现如果我的眼睛看不到我会很有帮助;)
naugtur

也有可能sudo需要。
colidyre

21

梳理弗拉德(和其他人)的出色研究:

在同一目录中创建以下两个文件,在路径中输入$ HOME / bin:

silence.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-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控制台中。顺便说一句,$!指的是最新的后台作业,但我认为它不能在脚本本身中使用。我使用别名: 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-终端写入
close(2)-关闭错误输出
/ tmp / myerrlog-文件至写入
q-退出gdb
bg%1-在后台运行停止的作业1
取消%1-从终端分离作业1


2
如果stdin(文件描述符0)被关闭,这将不起作用。
pabouk 2014年

这挽救了我的一天。我在ssl会话中进行了一次运行,前10%需要一个小时,而我真的不想让笔记本电脑再运行10个小时。但是我是否应该假设您对stderr的重定向应该阅读 p open("/tmp/myerrlog", 2)
GerardV

在CentOS 6上运行时有一个非常小的问题-文件“ / tmp / myerrlog”必须已经存在。当然,用触摸创建它很简单。
ebneter

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


0

您可以使用重定向(https://github.com/jerome-pouiller/reredirect/)。

类型

reredirect -m FILE PID

输出(标准和错误)将写入FILE。

reredirect README还说明了如何还原进程的原始状态,如何重定向到另一个命令或仅重定向stdout或stderr。

reredirect还提供了一个名为的脚本relink,该脚本允许重定向到当前终端:

relink PID
relink PID | grep usefull_content

(重定向似乎与另一个答案中描述的Dupx具有相同的功能,但它不依赖于Gdb)。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.