我正在尝试测试服务器上是否存在自定义防火墙规则。我通过使用 awk 处理 iptables 输出并返回适当的退出代码来完成此操作。这按预期工作,并打印了正确的结果。然而,当它通过管道传输到 tee 时,条件不再起作用,并且每次都会打印结果。
# The broken code
ssh server1 'sudo iptables -L | awk "(!/ACCEPT/ && !/^target/ && !/^$/) {rules=1 ;} (rules==1) {exit 1}" && printf "%s\n" "string1" "string2" | sudo tee -a /path/to/file'
# The same code without `| sudo tee -a /path/to/file` works as intended
ssh server1 'sudo iptables -L | awk "(!/ACCEPT/ && !/^target/ && !/^$/) {rules=1 ;} (rules==1) {exit 1}" && printf "%s\n" "string1" "string2"'
我缺少什么?我在 RHEL8 上使用 GNU coretils 8.30 和 GNU bash 版本 4.4.20(1)。我刚刚注意到 awk 语句可以缩短,但这不是重点:D
答案1
您的投诉主要围绕管道返回的状态iptables ... | awk ... && printf ... | tee ...
。狂欢文档
解释了它的价值是什么。
除非启用了 pipelinefail 选项,否则管道的返回状态是最后一个命令的退出状态。如果启用了pipefail,则管道的返回状态是最后一个(最右边)以非零状态退出的命令的值,如果所有命令成功退出,则返回零。
考虑执行
set -o pipefail
在管道结果之前。
或者,如果您发现更简单的话,可以使用... | (cmd ... || true) | ...
习惯用法,以便每个阶段都会返回。0
在检测到异常情况时发出独特的消息,以便稍后的管道阶段可以grep
处理它。
让 bash 有条件地协调 awk 的简单处理和 printf 的简单处理的设计决策在当时可能是有意义的,但现在您发现它拖累了生产力。考虑将该逻辑合并到一个简单的 awk 脚本中。
您可能会发现运行较短的管道更方便。
ssh server1 ... | awk ... > server1.txt
# awk exit status is now available in $? so you can conditionally run printf
cat server1.txt >> /path/to/file
在最初的问题中, /path/to/file 是一个服务器路径名,我们正在运行一个具有一定复杂性的单行远程管道。您可能会发现iptables -L
通过 ssh 连接返回原始输出并运行本地管道更方便。生成本地结果文件后,您始终可以将其存储为:
cat results.txt | ssh server1 'cat >> /path/to/file'