[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
posted @ 2024-12-12 17:52  Startu  阅读(18)  评论(0编辑  收藏  举报