将curl 的输出通过管道传输或重定向到ssh 命令

将curl 的输出通过管道传输或重定向到ssh 命令

我很确定这是不可能的,但我过去就错了。

基本上,我想做的就是从curl 命令获取JSON 输出并将其通过管道传输到jq 以保持美观。效果很好。但 OSX 没有它,必须通过brew 或其他方式安装。对我来说不是问题。但在企业环境中,安装第三方应用程序是不受欢迎的。但我们的大量 Linux 服务器都安装了 jq。

是否可以将本地输出通过管道传输到 ssh 来运行 jq?

curl https://whatever.site/json | ssh linuxHost "jq '.'" > output.json

需要注意的是,大量的 Linux 服务器没有外部访问权限,因此在那里运行curl 命令不起作用。是的,我可以做一个 scp,但是步骤更多。

答案1

如果你想做的只是漂亮的印刷品JSON 文档(没有花哨的过滤、聚合或其他东西),那么您可以使用json_pp( /usr/bin/json_pp),这是 macOS 基本系统实用程序中的一个更简单的实用程序。该json_pp实用程序是 Perl 模块的命令行包装器JSON::PP

$ echo '{ "hello": "world!" }' | json_pp
{
   "hello" : "world!"
}

请参阅 参考资料,了解man json_pp有关自定义输出格式(-json_opt选项)的说明。

例子:

$ echo '{ "hello": "world!" }' | json_pp -json_opt pretty,indent_length=2,space_before=0
{
  "hello": "world!"
}

答案2

是的,当您ssh使用命令运行时,ssh进入rsh(而不是rlogin)模式,而不是在伪终端中运行远程系统上用户的登录 shell,sshd而是使用连接到三个的 stdin、stdout 和 stderr 来运行指定的命令不同的管道。

这些管道的内容通过加密隧道传输,并从进程的 stdin、stdout 和 stderr 读取/写入ssh

所以在:

local-cmd | ssh 'remote-cmd' > file
  • ssh的 stdin 是local-cmd正在写入的管道的读数
  • ssh的标准输出是以file只写模式打开的。
  • ssh如果您从终端运行它,则 stderr 可能是终端设备

  • 从标准输入读取的内容ssh通过加密通道发送并remote-cmd通过管道输入。
  • 在标准输出上写入管道的内容remote-cmd通过加密通道发送sshd(从该管道的另一端读取),并ssh在接收后将其写入其标准输出,即写入文件。
  • remote-cmd与单独管道上的 stderr相同。

您可以使用 lsof 观察它:

$ sleep 3 | ((sleep 1; lsof -wc ssh -ad 0-99 +E >&2) <&- & ssh [email protected] 'lsof -wp "$$" -ad 0-2 +E; sleep 2') > file
COMMAND   PID     USER   FD   TYPE DEVICE SIZE/OFF    NODE NAME
screen   7561 chazelas    9u   CHR    5,2      0t0      89 /dev/ptmx ->/dev/pts/5 63562,zsh,0u 63562,zsh,1u 63562,zsh,2u 63562,zsh,10u 63608,sleep,0u 63608,sleep,2u 63609,ssh,2u 63609,ssh,6u 63610,lsof,1u 63610,lsof,2u
sleep   63608 chazelas    1w  FIFO   0,14      0t0  698683 pipe 63609,ssh,0r 63609,ssh,4r
ssh     63609 chazelas    0r  FIFO   0,14      0t0  698683 pipe 63608,sleep,1w 63609,ssh,4r
ssh     63609 chazelas    1u   CHR    1,3      0t0       5 /dev/null
ssh     63609 chazelas    2u   CHR  136,5      0t0       8 /dev/pts/5 7561,screen,9u
ssh     63609 chazelas    3u  IPv4 697921      0t0     TCP localhost:50006->localhost:ssh (ESTABLISHED)
ssh     63609 chazelas    4r  FIFO   0,14      0t0  698683 pipe 63608,sleep,1w 63609,ssh,0r
ssh     63609 chazelas    5w   REG   0,39      540 8488375 /home/chazelas/file
ssh     63609 chazelas    6u   CHR  136,5      0t0       8 /dev/pts/5 7561,screen,9u
~$ cat file
COMMAND   PID USER   FD   TYPE DEVICE SIZE/OFF   NODE NAME
sshd    63612 root    5w  FIFO   0,14      0t0 698269 pipe 63692,bash,0r 63695,lsof,0r
sshd    63612 root    7r  FIFO   0,14      0t0 698270 pipe 63692,bash,1w 63695,lsof,1w
sshd    63612 root   10r  FIFO   0,14      0t0 698271 pipe 63692,bash,2w 63695,lsof,2w
bash    63692 root    0r  FIFO   0,14      0t0 698269 pipe 63612,sshd,5w
bash    63692 root    1w  FIFO   0,14      0t0 698270 pipe 63612,sshd,7r
bash    63692 root    2w  FIFO   0,14      0t0 698271 pipe 63612,sshd,10r

您可以在其中看到 的sshfd 4 和 5(从 fds 0 和 1 复制)分别从 sleep 和 进入管道file,并且在远程端(那里的输出lsof最终出现在file)远程的 fds 0, 1, 2 shell有3个不同的管道,全部通向sshd

所以,这样一来,当涉及到标准 IO 时,运行ssh cmd在功能上与本地运行类似cmd,但仍然有一些要点需要注意:

  • 如上所示,cmd远程系统上的 stdin/stdout/stderr 都是管道,无论它们ssh在客户端上的用途如何(例如在我们的示例中分别为管道、常规文件和 tty 设备)。例如,如果没有重定向到file,您会注意到jq的输出不是彩色的,因为jq只有当它转到 tty 设备时才对其输出进行着色。
  • ssh将消耗其 stdin 上的输出并将其发送到 sshd,无论cmd远程主机上是否读取它,因此例如seq 1000 | (ssh localhost 'IFS= read -r one_line_only'; cat)不输出任何内容,因为即使远程read命令只读取一行,也已发送tossh的完整输出(大部分原因是没有被阅读),所以没有什么可读的。seqsshdreadcat

在这里,你还可以这样做:

curl... | ssh 'jq . > remotefile'

远程的输出jq存储在远程文件中(在远程用户的主目录中)。或者:

curl... | ssh 'jq . | tee remotefile' > localfile

(或者简单地说curl | ssh 'jq >&1 > remotefile' > localfile,如果远程用户的登录 shell 是 zsh),以便将输出jq存储在本地和远程。


至少在 openssh 的情况下,stdin 和 stdout 在不同的 fd 上重复正在使用哪些以及标准输出重新打开于/dev/null,但这是一个实现细节,在实践中没有什么区别。

相关内容