把一个进程的 stdout stdin 重新绑定到另一个控制台

另外在 Linux 有 reptyr 这个工具可以用。对 FreeBSD 只有有限的支持,还不能很好支持 macOS。

因为 macOS 用的是 lldb,而且没有 procfs,需要用 lsof 查看 fd,所以我改写了一份脚本。 注意,受系统完整保护的预装软件,比如 bash, tail 只有在关了 SIP 以后才能用这个方法处理。 (gdb 在 macOS 需要用 Keychain 签名才能用)

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

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

src="$1"; dst="$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"
# llbd 不接受 pipe 直接传脚本,可能我这里写错了 =_=
# 对 Bash 不太熟
    lldb -b -o $(
        echo "attach $pid"
        echo 'call (int)open("'$dst'", 66, 0666)'
        for ufd in $(LANG=C lsof -p $pid | \
                         grep "$src"\$ | awk ' { print $4; } ' | \
                         sed 's/.$//');
 	      do echo 'call (int)dup2($0,'"$ufd"')'; done
        echo 'call (int)close($0)'
        echo 'detach'; echo 'quit'
    )
done