[linux] 获取重定向到/dev/null的进程标准输出
一般来说,linux上后台运行的程序都会写类似于 ./foo 1>/dev/null 2>&1
以此实现不输出任何东西,但是如果需要用到标准输出用来调试,此时就会很要命
对于这样的程序,通过命令 ls -l /proc/<pid>/fd/
可以看到 1 已经指向/dev/null
了。
如果想要获取输出,可以通过以下步骤
gdb attach <pid>
call (int)dup2((int)open("/root/foo_stdout.log", 1089, 0777), 1)
- 退出gdb即可
此时可以看到指定的log文件已经创建了,通过ls -l /proc/<pid>/fd/
可以看到标准输出已经重定向到指定文件了。
原理就是通过gdb向程序插入代码并执行,dup2可以实现类似重定向的效果。如果需要重定向标准错误,把dup2的第二个参数改为2应该就可以了。
open的参数1089是 O_WRONLY | O_CREAT | O_APPEND 的结果,所以对log文件是追加效果,具体可参考:https://stackoverflow.com/questions/72071279/attaching-to-a-process-and-call-dup2-on-aarch64
之后可以通过如下脚本实现这样的功能,第一个参数是pid,第二个参数为指定文件
#!/bin/bash
# 脚本用于修改正在运行进程的标准输出和错误输出,依赖gdb,请保证gdb可用
# 第一个参数是进程pid,你可以使用pgrep <进程名>获取,第二个参数为输出文件,第三个参数为要修改的输出流,2为标准错误,1为标准输出,不提供这个参数默认为1
# 例如 ./notnull.sh 11223 /root/loader.log 2 会将11223进程的标准错误重定向到loader.log文件
if [ "$#" -lt 2 ]; then
echo "Usage: $0 <PID> <output_log> [fd]"
exit 1
fi
PID=$1
FILENAME=$2
if [ "$#" -eq 3 ]; then
FD=$3
else
FD=1
fi
if [[ "$FILENAME" != /* ]]; then
if command -v realpath >/dev/null 2>&1; then
FILENAME=$(realpath "$FILENAME")
elif command -v readlink >/dev/null 2>&1; then
FILENAME=$(readlink -f "$FILENAME")
else
echo "Error: realpath or readlink command is not available."
exit 1
fi
fi
if ! kill -0 $PID > /dev/null 2>&1; then
echo "Error: Process with PID $PID does not exist."
exit 1
fi
gdb -ex "attach $PID" -ex "call (int)dup2((int)open(\"$FILENAME\", 1089, 0777), $FD)" -batch