我有一台 Ubuntu 服务器,上面已经设置了 ssh 密钥。无需登录,我想从本地计算机上运行的 bash 脚本检查服务器上是否已安装给定的软件包。所需的输出是布尔值 0 或 1。到目前为止,我想出的命令是:
$ ssh root@myserver 'dpkg-query -W python3 | grep -c "no package found"'
0
此命令对于已安装的包和未安装的包均输出 0。
我退后一步,尝试在本地机器上实现这一点。发生了一些对我来说很奇怪的事情。
当我尝试时:
$ dpkg-query -W python
dpkg-query: no packages found matching python
当我尝试时:
$ dpkg-query -W python3
Desired=Unknown/Install/Remove/Purge/Hold
| Status=Not/Inst/Conf-files/Unpacked/halF-conf/Half-inst/trig-aWait/Trig-pend
|/ Err?=(none)/Reinst-required (Status,Err: uppercase=bad)
||/ Name Version Architecture Description
+++-==============-==============-============-=========================================================================
ii python3 3.8.2-0ubuntu2 amd64 interactive high-level object-oriented language (default python3 version)
这很好,因此对于“python”的情况,我希望将此输出通过管道传输到 grep 并检查其中是否存在类似“未找到包”的字符串。我运行以下命令,令人惊讶的是返回了 0。
$ dpkg-query -W python | grep -c "no packages found"
dpkg-query: no packages found matching python
0
这不应该返回单行 1 吗?为什么我已将其通过管道传输到另一个命令,但命令输出仍显示在终端上?我假设输出从未通过管道传输到 grep,因为我可以成功运行:
$ echo "dpkg-query: no packages found matching python" | grep -c "no packages found"
1
请解释为什么会发生这种情况,如果我因为困惑而问了一个琐碎的问题,请原谅我。
答案1
这是一条非常普遍的规则:命令的错误消息写入 stderr,而不是 stdout。管道仅将 stdout 重定向到另一个命令;stderr 仍与终端保持连接。dpkg-query: no packages found matching python
是一条错误消息,因此将其写入 stderr(即终端),并将 stdout 上的空输出通过管道传输到grep
,因此grep
返回 0。
如果你希望 stdout 和 stderr 都通过管道传输grep
,你应该使用类似
dpkg-query -W python 2>&1 | grep -c "no packages found"
您在问题中包含的命令的 ssh 版本也将不是按照您描述的方式工作。您还应该2>&1
像上面的命令一样在其中包含,否则它将完全按照本地版本工作 - 除非您的 ssh 客户端(或远程计算机上的服务器)以某种方式配置为将 stderr 重定向到 stdout,但这是不是标准配置。在我的例子中,ssh 版本的行为与本地版本完全相同。
dpkg-query
但是,您不必通过管道传输through的输出grep
(这可能依赖于语言环境,因此不可靠),而是只需使用命令的返回代码即可dpkg-query
。如果包已安装,则返回代码为 0,否则为 1。因此,您可以使用类似
dpkg-query -W python >& /dev/null ; echo $?
>& /dev/null
是否能从dpkg-query
(stdout 和 stderr) 中删除任何输出,因为我们只对返回代码感兴趣 ( $?
)。注意:>&
在 中有效bash
。如果您希望在 (例如在脚本中) 中使用它sh
,则应改用此方法:
dpkg-query -W python >/dev/null 2>&1 ; echo $?