147

In the shell you can do redirection, > <, etc., but how about AFTER a program is started?

Here's how I came to ask this question, a program running in the background of my terminal keeps outputting annoying text. It's an important process so I have to open another shell to avoid the text. I'd like to be able to >/dev/null or some other redirection so I can keep working in the same shell.

4
  • I know the easiest way to redirect the STDOUT/STDERR is to DUP2 their file descriptors BEFORE forking. This is a fairly standard practice, and probably the way shells accomplish it right now. Not sure if that gives an answer, but I'm thinking it diminishes the chances of there being a good one.
    – Stefan Mai
    Commented Feb 27, 2009 at 6:31
  • 2
    reptyr
    – lsl
    Commented Aug 19, 2013 at 7:01
  • 1
    You can use reredirect Commented Mar 24, 2021 at 13:01
  • Similar question on superuser: Redirect stdout while a process is running.
    – idbrii
    Commented May 28 at 14:28

8 Answers 8

137

Short of closing and reopening your tty (i.e. logging off and back on, which may also terminate some of your background processes in the process) you only have one choice left:

  • attach to the process in question using gdb, and run:
    • p dup2(open("/dev/null", 0), 1)
    • p dup2(open("/dev/null", 0), 2)
    • detach
    • quit

e.g.:

$ 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

You may also consider:

  • using screen; screen provides several virtual TTYs you can switch between without having to open new SSH/telnet/etc, sessions
  • using nohup; this allows you to close and reopen your session without losing any background processes in the... process.
13
  • 1
    Your gdb answer did not work with tail -f file, and it did not work with a test program in c compiled with gcc -ggdb that does a printf every second. Also cont makes it impossible to run more gdb commands, the command would be detach, then quit. Commented Feb 27, 2009 at 7:11
  • 13
    If you're redirecting stdout/stderr (to anything besides /dev/null apparently), you need to open the file with write access -- open("/path/to/new/stdout",O_WRONLY). O_WRONLY probably won't be available, though; its value is 1 on Linux/glibc.
    – Jander
    Commented Oct 21, 2010 at 5:34
  • 13
    A word of caution: attaching to a process in gdb pauses the process until you detach from it.
    – Marty B
    Commented Nov 14, 2011 at 3:20
  • 1
    Adding onto @Jander 's comment, using 1025 activates O_APPEND in addition to O_WRONLY, which is handy if you redirect both stderr and stdout to the same file.
    – spectras
    Commented Oct 17, 2016 at 10:54
  • 1
    After attaching (which I had to do as root even though the process was run as my current user), running the commands returned 'open64' has unknown return type; cast the call to its declared return type. I'm not sure if these are the right casts, but I was able to get it to work by casting both dup2 and open to int: p (int)dup2((int)open("/dev/null",0),1)
    – tobek
    Commented Apr 29, 2020 at 21:50
62

This will do:

strace -ewrite -p $PID

It's not that clean (shows lines like: write(#,<text you want to see>) ), but works!


You might also dislike the fact that arguments are abbreviated. To control that use the -s parameter that sets the maximum length of strings displayed.

It catches all streams, so you might want to filter that somehow:

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

shows only descriptor 1 calls. 2>&1 is to redirect STDERR to STDOUT, as strace writes to STDERR by default.

3
  • 6
    This is not what the OP asked for. OP asked to REDIRECT away from the TTY, not intercept. Also, on some platforms strace/truss will insert spaces between intercepted stream characters and/or escape non-ASCII, and you'll have to deal with processing those too.
    – vladr
    Commented Sep 4, 2010 at 16:24
  • 6
    Yes, this does the thing partially - but for some people reading this question it's all they need - to see what's happening in a program mistakenly run to write to null or on another console. I found it out after finding this question in the process and thought it's a nice hack (at least for me). AND quite a few people find it helpful if my eyes don't decieve me ;)
    – naugtur
    Commented Sep 6, 2010 at 8:35
  • It's also possible that sudo is needed.
    – colidyre
    Commented Mar 30, 2019 at 18:10
24

Redirect output from a running process to another terminal, file, or screen:

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

Inside gdb:

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

Detach a running process from the bash terminal and keep it alive:

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

Explanation:

  • 20818 - just an example of running process PID
  • p - print result of gdb command
  • close(1) - close standard output
  • /dev/pts/4 - terminal to write to
  • close(2) - close error output
  • /tmp/myerrlog - file to write to
  • q - quit gdb
  • bg %1 - run stopped job 1 on background
  • disown %1 - detach job 1 from terminal
  • [Ctrl+z] - stop the running process
  • [Ctrl+d] - exit terminal
4
  • 2
    This will not work if stdin (file descriptor 0) is closed. Commented Nov 24, 2014 at 20:35
  • 2
    This saved my day. I had a running make in an ssl-session taking an hour for the first 10% and I really didn't want to keep my laptop running for another 10 hours. But am I right to assume your redirect for stderr should read p open("/tmp/myerrlog", 2) ?
    – GerardV
    Commented Oct 9, 2016 at 9:53
  • Had a very minor issue with this running on CentOS 6 — the file "/tmp/myerrlog" had to already exist. It was trivial to create it with touch, of course.
    – ebneter
    Commented Jan 6, 2018 at 3:18
  • 12 years on from original post, this saved my day too trying to get logs / output from a process that was started using autostart. Thanks!
    – John U
    Commented Dec 8, 2022 at 15:41
21

riffing off vladr's (and others') excellent research:

create the following two files in the same directory, something in your path, say $HOME/bin:

silence.gdb, containing (from vladr's answer):

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

and silence, containing:

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

Now, next time you forget to redirect firefox, for example, and your terminal starts getting cluttered with the inevitable "(firefox-bin:5117): Gdk-WARNING **: XID collision, trouble ahead" messages:

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

You could also redirect gdb's output to /dev/null if you don't want to see it.

1
  • 3
    My gdb (v7.2) has a handy option --batch-silent which suppresses output and doesn't dump you into the gdb console if something goes wrong (eg missing process). BTW, $! refers to the most recent background job, but I don't think it can be used in the script itself. I use an alias: alias silencebg='silence $!'
    – seanf
    Commented Feb 12, 2013 at 5:25
5

You can use reredirect (https://github.com/jerome-pouiller/reredirect/).

Type

reredirect -m FILE PID

and outputs (standard and error) will be written in FILE.

reredirect README also explains how to restore original state of process, how to redirect to another command or to redirect only stdout or stderr.

reredirect also provide a script called relink that allows to redirect to current terminal:

relink PID
relink PID | grep useful_content

(reredirect seems to have same features than Dupx described in another answer but, it does not depend on Gdb).

3

Not a direct answer to your question, but it's a technique I've been finding useful over the last few days: Run the initial command using 'screen', and then detach.

3

Dupx is a simple *nix utility to redirect standard output/input/error of an already running process.

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

3

this is bash script part based on previous answers, which redirect log file during execution of an open process, it is used as postscript in logrotate process

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

updated: for gdb v7.11 and later, set scheduler-locking on or other any options mentioned here is required, because after attaching gdb, it does not stop all running threads and you may not able to close/open your log file because of file usage.

Not the answer you're looking for? Browse other questions tagged or ask your own question.