tags:

views:

4549

answers:

6

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.

+14  A: 

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.:

myuser@test:~$ tail -f /var/log/lastlog &
[1] 5636
myuser@test:~$ 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
myuser@test:~$ 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&gt;
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
myuser@test:~$ 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
myuser@test:~$

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.

Cheers, V.

vladr
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
Correct about detach, it's 2AM. :) What exactly did not work with the gdb solution?
vladr
My bad, not only did I have cont/detach wrong, but my dup2 was swapped. It should work now.
vladr
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 = 4then after detach, it simply keeps printing.
Ian Kelling
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
No, you passed the params to dup in the wrong order. Use dup2(open("/dev/null",0),1), see above
vladr
Nice. The order I had was copied from you. thanks for fixing and nice writeup.
Ian Kelling
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
A: 

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
A: 

The best answer would be to make use of some unused signals (i.e. SIGUSR1 / SIGUSR2)

Upon receiving a SIGUSR1, the program would know to re-direct stdout to NULL (or some pre-arranged file).

Upon receiving a SIGUSR2, the program would know to re-direct stderr to NULL (or some pre-arranged file)

Re-directing is very easily accomplished with the freopen() command. See this question. The handler should call fflush(), then re-assign stdout/stderr elsewhere.

So, foo --hush-out should find the running instance of foo and send it a SIGUSR1, likewise (but a different signal) for --hush-err.

If you are already using SIGUSR1/SIGUSR2, fear not, there are likely more not being handled that could be.

Of course, signals are not a very elegant example of IPC, however if some file is arranged at start up, that's not a big deal.

NULL is likely going to be /dev/null on your system, but such a character device can exist anywhere.

If for some reason you can't modify the program .. just use the screen trick, if your forced to use the local console.

Tim Post
+6  A: 

This will do:

strace -ewrite -p $PID

It's not that cleen (shows lines like: write(#,) ), but works! (and is single-line :D ) You might also dislike the fact, that arguments are abbreviated. To control that use -s parameter that sets the maxlength of strings displayed.

It catches all streams, so You might want to filter that somehow.

You can filter it:

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.

naugtur
This is a great solution.
Kyle W. Cartmell
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
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
A: 

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 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 stoped job 1 on background
disown %1 - detach job 1 from terminal

Mirek