如何将数据通过管道输入实用程序/命令作为 shell 的一部分here-doc?

如何将数据通过管道输入实用程序/命令作为 shell 的一部分here-doc?

以下示例是使用管道的一种经典方法。在管道的右侧,我们有一个“简单”实用程序,它从标准输入(管道)读取数据并以相反的顺序打印到标准输出:

{ cat <</EOF
Hello
World
/EOF
} | rev

显然结果是:

olleH
dlroW

现在我想通过管道将数据输入到命令/实用程序中,这是 shell 的一部分,here-doc:

{ cat <</EOF
Hello
World
/EOF
} | ksh -x <</EOF
rev
/EOF

这不起作用,没有错误消息,什么也没有,管道数据丢失,因为 ksh 将 stdin 重定向到here-doc。

我尝试了某种重定向:

set -x
{ cat <</EOF
Hello
World
/EOF
} | { exec 4<&0; ksh -x <</EOF; exec 0<&4 4<&-; }
rev <&4
/EOF

但这也不起作用,它在 rev 命令上显示错误,显然 stdin 到 fd4 的重定向没有传递到 ksh 的子进程:

+ exec
+ 4<& 0
+ ksh -x
+ cat
+ 0<< \/EOF
rev <&4
/EOF
+ 0<< \/EOF
Hello
World
/EOF
+ rev
+ ksh[1]: 4: cannot open [Ungültiger Dateideskriptor]
+ exec
+ 0<& 4 4<& -

我被困在这里了。

我的实际应用程序要复杂得多:在管道的左侧,我将通过 ssh 连接到源服务器,可以选择通过 sudo 连接到该服务器上的其他用户,并运行 tar 以通过管道传输一些数据文件。在管道的右侧,我必须 ssh 到目标服务器,可以选择 sudo 到不同的用户,最后运行 tar 以从管道接收数据。

顺便说一句:如果我不使用管道右侧的此处文档,则一切正常:

{ cat <</EOF
Hello
World
/EOF
} | ksh -xc "rev"

即使使用 ssh 加 sudo 加 tar 也可以正常工作。出于某种原因,我希望在右侧有一个 shell 的此处文档。正如您所看到的,ksh 是该应用程序中我最喜欢的 shell。

根据要求:我原来的声明是这样的

ssh $SSHOPT $INST_FILE_USER@$INST_FILE_HOST "cd $IDIR && { tar -cvf - $IFIL && echo RCSRC0 >&2 || echo RCSRC1 >&2; }" 2>$TMPFILE.err.src | ssh $SSHOPT $TRG_USER@$TRG_HOST "cd $TDIR && { tar -xmvf - && echo RCTRG0 || echo RCTRG1; }" 1>$TMPFILE.out.trg 2>&1

到目前为止一切正常。正如您所看到的,执行了最少的错误检查,这允许识别 tar 成功。现在我必须添加 sudo 功能,这意味着在 ssh 的 shell 和 cd 命令之间我必须添加一些代码来切换用户。难道真的是sudo,会用到吗?可能是 pbrun 命令,因为客户可能更喜欢 pbrun。如果使用此处文档而不是 1 个怪物语句,则可以更灵活地处理这些不同的场景。

5月21日更新: 我尝试遵循迄今为止给出的建议,但我仍然陷入困境。让我们看一下下面给出的答案,它对我来说效果很好:

echo yup | ksh -c 'exec 4<&0; ksh -c "rev <&5" 5<&4'
puy

如果我开始使用 ssh 或将 ssh 与 sudo 结合使用,我的重定向示例将不再起作用:

echo yup | ssh admin@trg14 ksh -c 'exec 4<&0; ksh -c "rev <&5" 5<&4'
bash: 4: Bad file descriptor

echo yup | ssh admin@trg14 sudo -n -u trg4 ksh -c 'exec 4<&0; ksh -c "rev <&5" 5<&4'
bash: 4: Bad file descriptor

我尝试了 ssh 和 sudo 组合的 2 个场景:第一个是使用 here-doc 作为 ksh 的输入:

场景一:

echo yup | ssh admin@trg14 sudo -n -u trg4 -- ksh<</EOF
whoami && whoami && rev && whoami
/EOF
trg4
trg4
trg4

到目前为止,这是有效的。注意:所有 4 个命令均以用户 trg4 身份运行,由于此处文档,rev 未按预期通过管道获得任何输入。

因此我尝试实现下面建议的解决方案:here-doc 将作为 ksh 的参数输入:

场景2:

echo yup | ssh admin@trg14 sudo -n -u trg4 -- ksh -c "$(cat<</EOF
whoami && whoami && rev && whoami
/EOF
)"
trg4
admin
puy
admin

rev 的输出符合预期,:whoami 向我们展示了在这个版本/实现中只有第一的命令以 sudo 用户身份运行,其余命令将以 ssh 登录用户身份运行!在 rev 的情况下没有问题,但如果您需要访问或写入数据库文件,您将因权限问题而失败。

从这 2 个示例得出的结论:场景 1 可能是在正确的 sudo 用户下运行所有​​命令的正确途径,但它需要解决 stdin 重定向的问题,以便能够使用管道。

任何帮助表示赞赏!

答案1

一个简单的解决方法是将 here-doc 脚本作为-c参数传递给内壳:

{ cat <<'/EOF'
Hello
World
/EOF
} | ksh -c "$(<<'/EOF'
rev
# you can freely use " inside this script
/EOF
)"

会给

olleH
dlroW

这也适用于zsh.更改$(<<$(cat<<应该让它工作任何壳。

您还可以摆弄重定向,前提是您在命令级别而不是使用以下命令进行重定向exec

... | ksh 4<&0 <<'/EOF'
rev <&4
/EOF

在这种情况下,您还可以使用 交换回here-doc脚本中的fds exec 5<&4 4<&0 <&5 5<&-,但恕我直言,所有这些都是毫无价值的复杂化。

ksh 中的脚本级重定向

第三个示例中的问题可以简化为更简单的测试用例:

$ echo yup | ksh -c 'exec 4<&0; ksh -c "rev <&4"'
ksh: 4: cannot open [Bad file descriptor]

但:

$ echo yup | dash -c 'exec 4<&0; ksh -c "rev <&4"'
puy

这是因为将在使用以下命令打开的任何 fd 上ksh设置 close-on-exec 标志 ( )O_CLOEXEC脚本级重定向(例如使用exec 4</path/toexec 4<&0)。这是由标准:

如果exec在没有命令或参数的情况下指定,并且使用关联的重定向语句打开任何编号大于 2 的文件描述符,则未指定当 shell 调用另一个实用程序时这些文件描述符是否保持打开状态。

这可以通过做额外的事情来解决命令级重定向:

echo yup | ksh -c 'exec 4<&0; ksh -c "rev <&5" 5<&4'
puy

使用ksh93(但不使用mksh早于R57[1] 的)简单地重新复制 fd 本身就可以工作:

$ echo yup | ksh93 -c 'exec 4<&0; ksh -c "rev <&4" 4<&4'
puy

[1]2019年3月发布,修复于此犯罪

答案2

[你应该问一个不同的问题不同的问题,而不是堆砌这个]

使用 [command] 参数运行 ssh

当您ssh使用[command]参数运行时,如 中ssh user@host foo bar baz,会发生的情况是它的参数将由空间到单个参数中,该参数将通过-c交换机传递到远程计算机上用户的登录 shell(如果该 shell 的/bin/sh,/bin/sh -c 'foo bar baz'将运行)。

现在,您的示例中会发生什么:

echo yup | ssh admin@trg14 ksh -c 'exec 4<&0; ksh -c "rev <&5" 5<&4'

ssh 的参数将被连接成一个字符串:

ksh -c exec 4<&0; ksh -c "rev <&5" 5<&4

并且删除机器上的 shell 将首先运行

ksh -c exec 4<&0

进而

ksh -c "rev <&5" 5<&4

作为两个单独的命令,当然第二个命令中的 fd 4 不指向任何内容,因为它仅在第一个命令中重定向。

相关内容