如何通过 SSH 使用 xargs 从远程 crontabs 中获取文本模式?

How to grep text patterns from remote crontabs using xargs through SSH?

我正在开发一个脚本来搜索通过 SSH 在一堆远程服务器上从 CRON 执行的脚本中的模式。

Script on client machine -- SSH --> Remote Servers CRON/Scripts

目前我无法获得正确的输出。

客户端机器上的脚本

#!/bin/bash
server_list=( '172.x.x.x' '172.x.x.y' '172.x.x.z' )
for s in ${server_list[@]}; do
    ssh -i /home/user/.ssh/my_key.rsa user@${s} crontab -l | grep -v '^#\|^[[:space:]]*$' | cut -d ' ' -f 6- | awk '{print }' | grep -v '^$\|^echo\|^find\|^PATH\|^/usr/bin\|^/bin/' | xargs -0 grep -in 'server.tld\|10.x.x.x'
done

这只给我 crontab 脚本的路径,而不是匹配的行和行号加上第一行以“grep:”关键字为前缀(下面的示例):

grep: /opt/directory/script1.sh
/opt/directory/script2.sh
/opt/directory/script3.sh
/opt/directory/script4.sh

如何获得正确的输出,即脚本路径加上行号加上匹配模式的行数?

远程 CRON 示例

OO 6 * * * /opt/directory/script1.sh foo 
30 6 * * * /opt/directory/script2.sh bar

远程脚本内容示例

1 ) 这将匹配 grep 模式

#!/bin/bash
ping -c 4 server.tld && echo "server.tld ()"

2 ) 这与 grep 模式不匹配

#!/bin/bash
ping -c 4 8.x.x.x && echo "8.x.x.x ()"

如果没有示例输入,真的很难看出您的脚本试图做什么。但是 cron 解析几乎肯定可以通过将其全部重构为单个 Awk 脚本来极大地简化。这是一个快速的尝试,显然没有办法测试。

#!/bin/sh
# No longer using an array for no good reason, so /bin/sh will work
for s in 172.x.x.x 172.x.x.y 172.x.x.z; do
    ssh -i /home/user/.ssh/my_key.rsa "user@${s}" crontab -l |
    awk '! /^#|^[[:space:]]*$/ &&  !~ /^$|^(echo|find|PATH|\/usr\/bin|\/bin\/)/ { print  }' |
    # no -0; use grep -E and properly quote literal dot
    xargs grep -Ein 'server\.tld|10.x.x.x'
done

您的命令不会将 null-delimited 数据输出到 xargs 所以可能直接的问题是 xargs -0 将所有文件名作为一个文件名接收,这显然不存在,并且您忘记在错误消息末尾包含“:未找到文件”。

grep -E 的使用是一个小技巧,可以启用更现代的正则表达式语法,它与 Awk 中的语法更相似,您不必反斜杠“或”管道等。

此脚本与您的原始脚本一样,运行s grep 在您 运行 SSH 脚本所在的本地系统上。如果您想 运行 远程服务器上的命令,您需要重构以将整个管道放在单引号或此处文档中:

for s in 172.x.x.x 172.x.x.y 172.x.x.z; do
    ssh -i /home/user/.ssh/my_key.rsa "user@${s}" <<\________HERE
        crontab -l |
        awk '! /^#|^[[:space:]]*$/ &&  !~ /^$|^(echo|find|PATH|\/usr\/bin|\/bin\/)/ { print  }' |
        xargs grep -Ein 'server\.tld|10.x.x.x'
________HERE
done

重构后的脚本在引用中包含足够的复杂性,您可能不想将其作为参数传递给 ssh,这需要您弄清楚如何在本地和远程引用字符串。然后将它作为标准输入传递更容易,这显然只是逐字传输。

如果出现“Pseudo-terminal 不会被分配,因为标准输入不是终端。”,请尝试使用 ssh -t。有时您需要添加多个 -t 选项才能完全摆脱此消息。