首页 > 解决方案 > 从另一个命令启动的 ssh-add 捕获输出

问题描述

这是我的问题的完整版本。我包括所有这些细节以防我的预感是错误的,但你可能想跳到下面的 tl;dr。

我正在尝试编写一个运行任意命令并捕获是否有任何输出打印到终端的函数。我不想干扰正在打印的输出。如果这是一个相关的并发症(可能不是),我还想在命令的退出代码上进行分支。

这是我所拥有的:

function run_and_inspect {
    # this subshell ensures the stdout of ${@} is printed and captured
    if output=$(
        set -o pipefail
        "${@}" | tee /dev/tty
    ); then
        did_cmd_work="yes"
    else
        did_cmd_work="no"
    fi

    if [ -z "$output" ]; then
        was_there_output="no"
    else
        was_there_output="yes"
    fi

    echo "output?" $was_there_output
}

通常这工作正常:

$ run_and_inspect true
output? no

$ run_and_inspect "echo hello"
hello
output? yes

但是我发现了一个有问题的命令:

git pull | grep -v 'Already up to date.'

如果没有东西可以拉,这个管道通常不会产生任何输出。但是,如果 ssh-add 需要提示输入密码,则会有输出。它只是没有被注意到run_and_inspect

function git_pull_quiet {
    git pull | grep -v 'Already up to date.'
}

$ run_and_inspect git_pull_quiet
Enter passphrase for key '/home/foo/.ssh/id_ed25519':
output? no

我的终端有输出。我认为问题在于它不是来自git pull管道的标准输出,这是run_and_inspect众所周知的。它从哪里来的?我该如何解决?我也尝试过重定向stderr(即git pull 2>&1),但没有运气。有什么方法可以直接监控 /dev/tty 吗?

tl;博士(我想!)

认为这个问题归结为:为什么 log.txt 中没有密码提示?

$ git pull 2>&1 | tee log.txt
Enter passphrase for key '/home/foo/.ssh/id_ed25519':
Already up to date.
$ cat log.txt
Already up to date.

标签: bashstdoutssh-agent

解决方案


为什么 log.txt 中没有密码提示?

使用readpass.c read_passphrase()openssh load_identify_file打印提示。该函数使用_PATH_TTY "/dev/tty"然后s 到它。open(_PATH_TTYwrite()

输出直接显示到终端/dev/tty,而不是标准流。

就像你对 your 所做的那样tee /dev/tty,这意味着你的函数也将不起作用。更喜欢保留子程序的标准输出并保留缓冲:

if { tmp=$("$@" > >(tee >(cat >&3))); } 3>&1; then

有什么方法可以直接监控 /dev/tty 吗?

编写您自己的终端仿真器并在其中生成您的进程,然后解析该终端仿真器内的所有输入。类似screentmux可能有用的程序。

解决方法可能是下载proot并打开某个文件的文件描述符,然后创建一个仅/dev/tty包含文件符号链接的 chroot 并在该 chroot 内/proc/self/fd/<that file descriptor>运行该进程。proot这个想法是该进程将看到/dev/tty替换文件的chroot,并将写入您的文件描述符而不是终端。


推荐阅读