144

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.

tijko
  • 7,599
  • 11
  • 44
  • 64
Ian Kelling
  • 9,643
  • 9
  • 35
  • 39
  • 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 Feb 27 '09 at 06:31
  • 2
    [reptyr](https://github.com/nelhage/reptyr) – lsl Aug 19 '13 at 07:01
  • 1
    You can use [reredirect](https://github.com/jerome-pouiller/reredirect/) – Jérôme Pouiller Mar 24 '21 at 13:01

8 Answers8

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.
Martin Tournoij
  • 26,737
  • 24
  • 105
  • 146
vladr
  • 65,483
  • 18
  • 129
  • 130
  • 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. – Ian Kelling Feb 27 '09 at 07:11
  • Correct about detach, it's 2AM. :) What exactly did not work with the gdb solution? – vladr Feb 27 '09 at 07:27
  • My bad, not only did I have cont/detach wrong, but my dup2 was swapped. It should work now. – vladr Feb 27 '09 at 07:38
  • attaching gdb with: gdb a.out 12343(the pid) (gdb) p dup2(1, open("/dev/null", 0)) $1 = 3 (gdb) p dup2(2, open("/dev/null", 0)) $2 = 4 then after detach, it simply keeps printing. – Ian Kelling Feb 27 '09 at 07:43
  • er, that got all mangled. Basically, after the dup2s, there was output "$1 = 3" and "$2 = 4". And then it simply did not change anything after detach. – Ian Kelling Feb 27 '09 at 07:44
  • No, you passed the params to dup in the wrong order. Use dup2(open("/dev/null",0),1), see above – vladr Feb 27 '09 at 08:05
  • Nice. The order I had was copied from you. thanks for fixing and nice writeup. – Ian Kelling Feb 27 '09 at 09:14
  • 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 Oct 21 '10 at 05:34
  • 13
    A word of caution: attaching to a process in gdb pauses the process until you detach from it. – Marty B Nov 14 '11 at 03: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 Oct 17 '16 at 10:54
  • I added a bash function to automate this process with some minimal error handling and with a different detault to actually capture the output to a file. – Pushpendre Nov 15 '19 at 21:27
  • 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 Apr 29 '20 at 21:50
  • Just want to note that `screen` and `tmux` are quite an overkill for this purpose. If you're a minimalist, you can use `abduco`. However, you still have to think in advance, so that's not exactly what you were asking for. – pallly Jan 05 '21 at 19:25
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.

slhck
  • 36,575
  • 28
  • 148
  • 201
naugtur
  • 16,827
  • 5
  • 70
  • 113
  • 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 Sep 04 '10 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 Sep 06 '10 at 08:35
  • It's also possible that `sudo` is needed. – colidyre Mar 30 '19 at 18:10
23

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
armanexplorer
  • 93
  • 2
  • 5
Mirek
  • 231
  • 2
  • 2
  • 2
    This will not work if `stdin` (file descriptor `0`) is closed. – pabouk - Ukraine stay strong Nov 24 '14 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 Oct 09 '16 at 09: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 Jan 06 '18 at 03: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 Dec 08 '22 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.

jcomeau_ictx
  • 37,688
  • 6
  • 92
  • 107
  • 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 Feb 12 '13 at 05:25
4

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 usefull_content

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

Jérôme Pouiller
  • 9,249
  • 5
  • 39
  • 47
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.

Mostafa Nazari
  • 616
  • 7
  • 21
3

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

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

eMPee584
  • 1,945
  • 20
  • 20
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.

Roger Lipscombe
  • 89,048
  • 55
  • 235
  • 380