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.