我很确定这是不可能的,但我过去就错了。
基本上,我想做的就是从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
您可以在其中看到 的ssh
fd 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
的完整输出(大部分原因是没有被阅读),所以没有什么可读的。seq
sshd
read
cat
在这里,你还可以这样做:
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
,但这是一个实现细节,在实践中没有什么区别。