如何在子进程的标准输入的开头注入数据?

如何在子进程的标准输入的开头注入数据?

TL;DR 如何修复此命令行模板,当 cat(在客户端)与提示输入密码的 ssh 竞争时该模板会失败?

{ echo some stuff; cat; } | ssh SERVER cat
{ echo some stuff; cat; } | ssh SERVER ls

我不是在询问创建公钥/私钥。我不想提前使用标准输入,因为远程命令可能会忽略其标准输入(在这种情况下,强制用户输入 ^D 很痛苦)。我不希望用户必须为模板指定一个选项来指示远程命令是否读取其标准输入。我希望模板能够工作,无论远程命令是否读取其标准输入。


详细信息:我想为通过 ssh 远程运行的命令编写客户端和服务器端包装器。所讨论的命令可以是用户选择的任何命令。包装器的目的是设置命令的环境。客户端包装器需要向服务器端包装器发送少量数据,但我不想在命令行上发送该数据(我想在标准输入上发送它)。

以下是如何调用它的示例:

$ wrapper1 HOST wrapper2 sh -c "cat >file"
This data is sent to the remote side
^D

Wrapper1 计算一些数据,将其传递给wrapper2,后者在执行 sh 之前设置环境。 Cat 从标准输入读取并写入远程端的文件。

为了实现此目的,我希望客户端包装器在发送到服务器端包装器的标准输入流的开头注入一些数据。在运行真正的命令之前,服务器端包装器将消耗来自 stdin 的额外数据。

发送和使用注入的数据后,我希望远程命令的标准输入管道与没有此数据注入时一样。远程命令可能会也可能不会从其标准输入中读取任何内容——这取决于正在运行的特定命令。

由于事先不知道正在运行的命令,并且提前不知道是否需要从标准输入读取任何内容,因此我不希望客户端在调用远程命令之前消耗其整个标准输入。

以下是我正在尝试做的更多细节:

我想在“ssh HOSTwrapper2CMD”周围编写wrapper1,其中CMD事先未知(假设它不需要tty,但它可能会也可能不会读取stdin)。在wrapper2执行CMD之前,Wrapper1将向远程端发送一些额外的数据以供wrapper2读取。我希望wrapper1或多或少是透明的;它应该像 ssh 一样工作,除了发送额外数据并运行wrapper2作为远程命令以在执行CMD之前读取额外数据。如果远程 CMD 读取 stdin,我希望它获取wrapper1 的 stdin 的内容,就像运行“ssh HOST CMD”一样。

我通过让wrapper1创建一个子进程来编写此代码,该子进程将额外的数据写入标准输出,然后执行cat将标准输入复制到标准输出。 Wrapper1 将子进程的输出通过管道传输到 ssh 命令的标准输入中。但是,当 ssh 提示输入密码时,此方法会失败,因为 cat 和 ssh 都在争夺来自键盘的输入(当 CMD 交互运行时)。如果我在孩子的猫之前插入睡眠,它会起作用,但显然这不是最佳的。

因为我根本不知道 CMD 是否读取 stdin,所以我无法在执行 ssh 之前消耗所有 stdin 以避免冲突。无论如何,在许多场景下读取所有 stdin 都不是最优的(例如当它非常大时,或者需要增量消耗时)。

有什么办法可以做我想做的事吗?现在我能想到的就是wrapper1的一个选项,它提供了以下选择:

  1. 消耗整个标准输入并将其发送到远程端,或者
  2. 从 /dev/null 重定向标准输入。

但是,最好避免让用户选择这两个选项之一,并允许增量读取来自 stdin 的数据,就像通常没有包装器一样。

答案1

因此,您可以尝试设置 CMD,以便它始终将文件重定向为 STDIN,如下所示

cmd < file.txt

或者也许先读取文件,清理然后将其作为字符串发送

cmd <<< "string"

然后,当您调用 CMD 脚本时,您可以在调用 ./wrapper2 之前将参数按原样插入到文件中。当不需要 STDIN 时,请确保您不会通过在执行后自动删除临时文件或截断它来发送错误的内容。

答案2

不确定我是否正确理解了这个问题:

wrapper1 () {
    host="$1"
    shift
    {
         echo "nonsensical statement involving wrapper2"
         cat
    } | ssh $host wrapper2 "$@"
}

也许要注意缓冲。

相关内容