Redirect Output of an Already Running Process

Have you ever started a long running command and then wanted to put it in the background but it's sending its output to STDOUT? Here's a script that uses gdb to swap a processes file descriptors so you can send all that output to a file, or /dev/null after the fact. It may not be the most elegant solution, so any suggestions on ways to improve it are welcome!

#!/bin/bash
#
# Swap/Roll a logfile
#
# Usage:              <old logfile>  <new logfile> [ optional pids ]
#         ./swap.sh /var/log/logfile /tmp/logfile  [pids]
#
# Author: Robert McKay <rob...@mckay.com>
# Date:   Tue Aug 14 13:36:35 BST 2007
#
# Update: Added usage message when needed, a fuser format fix,
#         some whitespace cleanup, and a localization fix.
#         Ingvar Hagelund <ingvar@redpill-linpro.com>
# Date:   Sat Jul 10 02:11:49 CEST 2010
# Update: Dereference symlinks provided as src or dst
#         Better quoting an escaping of variables
#         Cleanup and simplify
#         John Westlund <john...@westlund.us>
# Date:   Sat May 17 18:27:50 PDT 2014

if [ "$2" == "" ]; then
    echo "Usage: $0 /path/to/oldfile /path/to/newfile [pids]
Example: $0 /var/log/somedaemon.log /var/log/newvolume/somedaemon.log 1234
Example: $0 /dev/pts/53 /dev/null 1234
"
    exit 0
fi

if ! gdb --version > /dev/null 2>&1; then
    echo "Unable to find gdb."
    exit 1
fi

src="$(readlink -f "$1")"
dst="$(readlink -f "$2")"
shift; shift
pids="$@"

for pid in ${pids:=$( /sbin/fuser "$src" | cut -d ':' -f 2 )};
do
    echo "src=$src, dst=$dst"
    echo "$src has $pid using it"
    (
        echo "attach $pid"
        echo 'call open("'$dst'", 66, 0666)'
        pushd /proc/$pid/fd/ > /dev/null 2>&1
        LANG=C
        for ufd in *; do
                if [[ "$(readlink $ufd)" == "$src" ]]; then
                        echo 'call dup2($1,'"$ufd"')'
                fi
        done
        popd > /dev/null 2>&1
        echo 'call close($1)'
        echo 'detach'
        echo 'quit'
        sleep 5
    ) | gdb -q -x -
done

You can use it like this:

# cat output.sh
echo $$
while true; do date; sleep 1; done

# bash output.sh
24468
Sun Apr 27 18:28:00 PDT 2014
Sun Apr 27 18:28:01 PDT 2014
Sun Apr 27 18:28:02 PDT 2014
^Z
[2]+  Stopped                 bash output.sh

# ls -l /proc/24468/fd/1
lrwx------ 1 root root 64 Apr 27 18:28 /proc/24468/fd/1 -> /dev/pts/0

# /admin/scripts/swap_fds.sh /dev/pts/0 /tmp/test.out 24468
src=/dev/pts/0, dst=/tmp/test.out
/dev/pts/0 has 24468 using it
-: No such file or directory.
(gdb) Attaching to process 24468

Program received signal SIGTSTP, Stopped (user).
Reading symbols from /usr/bin/bash...Reading symbols from /usr/bin/bash...(no debugging symbols found)...done.
(no debugging symbols found)...done.
Reading symbols from /lib64/libtinfo.so.5...Reading symbols from /lib64/libtinfo.so.5...(no debugging symbols found)...done.
(no debugging symbols found)...done.
Loaded symbols for /lib64/libtinfo.so.5
Reading symbols from /lib64/libdl.so.2...(no debugging symbols found)...done.
Loaded symbols for /lib64/libdl.so.2
Reading symbols from /lib64/libc.so.6...(no debugging symbols found)...done.
Loaded symbols for /lib64/libc.so.6
Reading symbols from /lib64/ld-linux-x86-64.so.2...(no debugging symbols found)...done.
Loaded symbols for /lib64/ld-linux-x86-64.so.2
0x00000031614bb10a in waitpid () from /lib64/libc.so.6
Missing separate debuginfos, use: debuginfo-install bash-4.2.45-1.fc18.x86_64
(gdb) $1 = 3
(gdb) $2 = 0
(gdb) $3 = 1
(gdb) $4 = 2
(gdb) $5 = 0
(gdb) Detaching from program: /usr/bin/bash, process 24468
warning: cannot close "/lib64/libtinfo.so.5": Invalid operation
(gdb) 

# bg
[2]+ bash output.sh &

# cat test.out
Sun Apr 27 18:29:49 PDT 2014
Sun Apr 27 18:29:50 PDT 2014

Above I've put together a simple test script that outputs its PID and then every second prints the date to STDOUT. After I launch the script I want to redirect the output. First I background it with Ctrl-Z. I already know the output is going to pts 0, but in case I wasn't sure I list the STDOUT filedescriptor for my script's PID. Above I've listed the symlink in proc that points to /dev/pts/0. I've updated the script to dereference this symlink, but above I've done it manually. After that I send the script to the background and check that my output has started to show up in my new output file.