![Bash 中的条件管道(自定义脚本)](https://linux22.com/image/1628270/Bash%20%E4%B8%AD%E7%9A%84%E6%9D%A1%E4%BB%B6%E7%AE%A1%E9%81%93%EF%BC%88%E8%87%AA%E5%AE%9A%E4%B9%89%E8%84%9A%E6%9C%AC%EF%BC%89.png)
我如何通过管道传输命令,并在脚本内部决定是否应该中断管道。
它类似于这个问题。但awk
我更愿意使用自己的脚本:
check_ip | update_bind9
check_ip
应该找到外部 IP(curl -s ifconfig.co
)并将其与存储在文件中的 IP 进行比较(~/.ip
)。
以下是主要问题:
如果 IP 已更改,check_ip
则应通过管道传递 IP。如果没有,则应断开管道。
这是否check_ip
会破坏管道,或者update_bind9
如果没有使用 IP 调用则会忽略调用?
我做了一些测试,调用:check_ip | echo "ok"
。
我试过exit 0
,exit 1
,return true
。return false
试过set -e
。没有成功。
答案1
管道的所有组件同时开始– 无论第一个命令中发生什么,第二个命令始终都会启动。
因此,第二个命令需要处理空的标准输入。
但我首先要说的是,管道并不是一个好的选择。
(也许你把|
(管道)和||
(布尔或)混淆了?后者是通常用作 if/then 的较短替代,例如,x || y
仅当 x 失败时才会调用 y - 类似地,x && y
仅当 x 成功时才会调用 y。)
通常,您应该使用 if/then 块。例如,如果check_ip
地址不同,if 本身返回 0(成功),如果地址相同,则返回 1(失败),如下所示:
if addr=$(check_ip); then
echo "$addr" | update_bind9
fi
或者,如果 update_bind9 可以自行确定地址(无需读取 stdin),则会如下所示:
if check_ip_needs_updating; then
update_bind9
fi
答案2
其他答案很好。但是如果你真的需要有条件地进行管道传输,那么你可能会发现ifne
有用:
ifne
当且仅当标准输入不为空时,才运行以下命令。
在你的情况下它将像check_ip | ifne update_bind9
。
很可能ifne
默认情况下未安装在您的 Linux 中。在我的 Debian 10 中,它在moreutils
包中。有人可能会认为额外的工具完全是多余的,因为我们可以将输出存储check_ip
在变量中并有条件地重用它,就像提到的答案中一样:
if addr=$(check_ip); then echo "$addr" | update_bind9 fi
条件可能是[ -n "$addr" ]
,无论如何;重要的是我们使用变量。像上面的代码可能会解决您的问题,但通常至少存在三个问题。假设command1 | command2
而不是check_ip | update_bind9
。解决方案概括为:
if foo=$(command1); then
echo "$foo" | command2
fi
进而:
foo=$(command1)
将从 的输出中删除所有尾随的换行符command1
。然后echo "$foo"
(或printf … "$foo"
) 将添加一个换行符(或分别添加固定数量的换行符)。因此command2
可能无法从 获得准确的输出command1
。command1
可能会产生包含 NUL 字符的输出。Bash 无法将 NUL 存储在变量中(大多数 shell 不能;zsh 可以)。如果出现 NUL,则command2
不会从 获得准确的输出command1
。command1
可能会产生巨大甚至是无穷无尽的流,因此将其存储在变量中可能无法实现。
使用文件而不是变量可以解决除最后一个问题之外的所有问题。command1 | ifne command2
对所有三个问题均无影响。