PGREP_OUTPUT=$(ssh -o ConnectTimeout=20 -o StrictH...
생성일: 2026년 1월 8일
생성일: 2026년 1월 8일
PGREP_OUTPUT=RANGER_HOST" "pgrep -f 'PROCESS_NAME' 2>&1") PGREP_EXIT_CODE=?
echo "pgrep输出: 'PGREP_OUTPUT'" >&2 echo "pgrep退出码: PGREP_EXIT_CODE" >&2
if [ PGREP_EXIT_CODE -eq 0 ] && [ -n "PGREP_OUTPUT" ]; then
PROCESS_STATE="UP"
echo "进程状态: PGREP_OUTPUT)" >&2
else
PROCESS_STATE="DOWN"
echo "进程状态: PROCESS_NAME)" >&2
fi
host上没有进程,但是 却有output 。 我手动执行也没有输出
你这段逻辑里,“host 上没进程但却有 output”,最常见就两类原因:
$PROCESS_NAME 实际为空/不对(导致 pgrep 匹配到别的东西)PROCESS_NAME=""(空串),pgrep -f "" 会匹配几乎所有进程,肯定有输出,而且退出码是 0。建议你先加一行确认变量:
bashecho "PROCESS_NAME=[$PROCESS_NAME], len=${#PROCESS_NAME}" >&2
并且在判断里把“输出必须是 PID(纯数字)”作为条件之一(避免误把 banner/提示当 PID)。
2>&1 会把“非 PID 输出”也抓进来你现在是:
bashPGREP_OUTPUT=$(ssh ... "pgrep -f '$PROCESS_NAME' 2>&1")
这会把这些也抓进 PGREP_OUTPUT:
所以即使没进程,PGREP_OUTPUT 也可能是“文字”,-n 就成立了。
同时把 ssh 的噪声降到最低,并要求输出必须是纯数字 PID:
bash# 降低 ssh 噪声:-q + LogLevel=ERROR;-T 禁用伪终端 PGREP_OUTPUT=$(ssh -T -q -o LogLevel=ERROR -o ConnectTimeout=20 -o StrictHostKeyChecking=no "$RANGER_HOST" \ "pgrep -f -- \"$PROCESS_NAME\"" 2>/dev/null) PGREP_EXIT_CODE=$? echo "pgrep stdout: [$PGREP_OUTPUT]" >&2 echo "pgrep exit: $PGREP_EXIT_CODE" >&2 # 只有当退出码=0 且输出里存在“纯数字 PID”才算 UP if [ "$PGREP_EXIT_CODE" -eq 0 ] && echo "$PGREP_OUTPUT" | grep -Eq '^[0-9]+([[:space:]][0-9]+)*$'; then PROCESS_STATE="UP" echo "进程状态: $PROCESS_STATE (PID: $PGREP_OUTPUT)" >&2 else PROCESS_STATE="DOWN" echo "进程状态: $PROCESS_STATE (未找到进程 $PROCESS_NAME)" >&2 fi
关键点:
2>/dev/null:把 ssh/pgrep 的错误输出丢掉,不污染PGREP_OUTPUTgrep -Eq '^[0-9]+...':确保 output 真的是 PID,而不是文字
这样你既能判断 PID,又能记录错误原因:
bashSSH_STDERR=$(mktemp) PGREP_OUTPUT=$(ssh -T -o ConnectTimeout=20 -o StrictHostKeyChecking=no "$RANGER_HOST" \ "pgrep -f -- \"$PROCESS_NAME\"" 2>"$SSH_STDERR") PGREP_EXIT_CODE=$? echo "stdout(PID候选): [$PGREP_OUTPUT]" >&2 echo "stderr: [$(cat "$SSH_STDERR")]" >&2 echo "exit: $PGREP_EXIT_CODE" >&2 rm -f "$SSH_STDERR" if [ "$PGREP_EXIT_CODE" -eq 0 ] && echo "$PGREP_OUTPUT" | grep -Eq '^[0-9]+([[:space:]][0-9]+)*$'; then PROCESS_STATE="UP" else PROCESS_STATE="DOWN" fi
你写的是 "pgrep -f '$PROCESS_NAME' ..."
这会让 远端收到的 pattern 带单引号(并且不做远端展开)。更标准的是用 -- + 双引号:
pgrep -f -- "$PROCESS_NAME"pgrep -f '$PROCESS_NAME'(容易引入字面量、奇怪匹配、调试困难)把 output 打印成“可见格式”,你就能一眼看出是不是 banner/文字/空格:
bashprintf 'RAW=[%s]\n' "$PGREP_OUTPUT" >&2 printf 'ESCAPED=%q\n' "$PGREP_OUTPUT" >&2
如果 ESCAPED 里不是数字,而是类似 Last\ login...、Warning:、Connection... 之类,那就完全印证了第 2 点。
如果你把你那次“有 output 的内容”(ESCAPED=%q 那行)贴出来,我可以直接告诉你它具体来自 ssh 哪一类输出,以及最小改动怎么彻底根治。
PGREP_OUTPUT=RANGER_HOST" "pgrep -f ${PROCESS_NAME}" 2>/dev/null )
这一句话里面 单双引号 会发生什么? 单引号 是否不会进行解码?
这个问题问得非常好 👍,正好踩在 shell 引号 + ssh + 远端 shell 的关键点上。我们一步一步精确解释你这句代码里到底发生了什么。
bashPGREP_OUTPUT=$(ssh -o ConnectTimeout=20 -o StrictHostKeyChecking=no "$RANGER_HOST" "pgrep -f ${PROCESS_NAME}" 2>/dev/null )
这里涉及两个 shell:
$RANGER_HOST 后执行的命令)"pgrep -f ${PROCESS_NAME}" 会发生什么?
${PROCESS_NAME}会在本地 shell 中先展开,然后再把结果作为字符串发送给远端执行。
本地 shell 解析
bash"pgrep -f ${PROCESS_NAME}"
${PROCESS_NAME} → 本地变量替换bashpgrep -f my_process
ssh 发送给远端
bashpgrep -f my_process
远端 shell 执行
${PROCESS_NAME} 这个变量如果 PROCESS_NAME 包含空格或特殊字符:
bashPROCESS_NAME="java -jar app.jar"
那么本地展开后会变成:
bashpgrep -f java -jar app.jar
远端实际执行等价于:
bashpgrep -f java -jar app.jar # -jar 被当成 pgrep 的参数了(错)
bashssh ... "pgrep -f '${PROCESS_NAME}'"
${PROCESS_NAME} 仍然会在本地展开bashssh ... "pgrep -f 'my_process'"
bashpgrep -f 'my_process'
此时:
❌ 单引号并不能阻止本地展开
只能阻止“它所在 shell”的展开
| 写法 | ${PROCESS_NAME} 在哪里展开 |
|---|---|
"pgrep -f ${PROCESS_NAME}" | 本地 shell |
"pgrep -f '${PROCESS_NAME}'" | 本地 shell |
'pgrep -f ${PROCESS_NAME}' | ❌ 不展开(但这条命令几乎没用) |
bashPGREP_OUTPUT=$(ssh -o ConnectTimeout=20 -o StrictHostKeyChecking=no "$RANGER_HOST" \ "pgrep -f -- \"\$PROCESS_NAME\"" 2>/dev/null)
但这要求:
PROCESS_NAME(通常并没有)bashPGREP_OUTPUT=$(ssh -T -o ConnectTimeout=20 -o StrictHostKeyChecking=no "$RANGER_HOST" \ "pgrep -f -- $(printf '%q' "$PROCESS_NAME")" 2>/dev/null)
作用:
printf '%q' 在本地把字符串安全转义*、不怕 [、不怕 |结合你前面的现象,最可能原因是:
PROCESS_NAME 为空或异常
bashpgrep -f
→ 匹配所有进程(一定有输出)
PROCESS_NAME 含空格,被拆成多个参数
bashpgrep -f java -jar xxx
→ -jar 被当成参数,行为异常
单引号不会“阻止解码”,只阻止“当前 shell 的展开”
ssh 命令里的变量,几乎永远是“先在本地展开,再给远端执行”
如果你愿意,我可以帮你写一个 **09 行以内的 **「ssh + pgrep 标准模板」,以后直接拷就不会再踩这个坑。
PGREP_OUTPUT=RANGER_HOST" "pgrep -f ${PROCESS_NAME}" 2>/dev/null )
2>/dev/null 是什么意思? 为什么 1> 都没值,2还有值?
这是个非常关键的 shell 基础点,而且你这个问题问得很到位 👍
我直接用结论 → 原理 → 结合你这行代码来解释。
2>/dev/null 是什么意思?(先给结论)bash2>/dev/null
意思是:
把「标准错误输出(stderr,FD=2)」重定向到
/dev/null(丢弃)
也就是说:
1>、2> 到底是什么?在 Unix/Linux 里,每个进程默认有 3 个输出通道(文件描述符 FD):
| FD | 名称 | 默认指向 |
|---|---|---|
| 0 | stdin | 键盘 |
| 1 | stdout | 终端 |
| 2 | stderr | 终端 |
| 写法 | 含义 |
|---|---|
> | 等价于 1>(标准输出) |
1>file | stdout → file |
2>file | stderr → file |
2>&1 | stderr → stdout |
1>&2 | stdout → stderr |
1> 没值,但 2 还有值」?这是你真正困惑的地方 👇
答案一句话:
有些程序(包括 ssh)故意把信息写到 stderr,而不是 stdout
bashssh host "false"
false:返回非 0比如:
Warning:Connection closedPermission denied这些 全是 stderr(FD=2)
bashssh host "echo ok; echo err >&2"
结果:
textok ← stdout (FD=1) err ← stderr (FD=2)
bashPGREP_OUTPUT=$(ssh ... "pgrep -f ${PROCESS_NAME}" 2>/dev/null)
stdout(FD=1)
pgrep 的 匹配结果(PID)$(...) 捕获进 PGREP_OUTPUTstderr(FD=2)
2>/dev/null 丢弃「没进程,但却有 output」
因为你之前用的是:
bashpgrep ... 2>&1
这会导致:
| 原本 | 结果 |
|---|---|
| stderr(文字、警告) | 合并进 stdout |
| stdout | 进入变量 |
判断 [ -n "$PGREP_OUTPUT" ] | 被误判为“有进程” |
2>/dev/null是把「ssh 的所有输出」都丢掉
2>/dev/null只丢 stderr,不影响 stdout
没看到
1>,所以 stdout 没内容
stdout 默认就是终端 / 命令替换,不需要写
1>
但我仍然建议一个最终版模板(防空格、防误判):
bashPGREP_OUTPUT=$(ssh -T -q \ -o LogLevel=ERROR \ -o ConnectTimeout=20 \ -o StrictHostKeyChecking=no \ "$RANGER_HOST" \ "pgrep -f -- $(printf '%q' "$PROCESS_NAME")" \ 2>/dev/null )
stdout 是“程序的结果”
stderr 是“程序的状态、抱怨、提示”
2>/dev/null就是:别抱怨,闭嘴干活
如果你愿意,我可以再给你画一个 FD 流向示意表,或者用 strace 角度解释为什么 ssh 这么设计。
我能在 shell 试试一下,echo 一下 2>&1 看一下具体的值
可以,而且这是理解 stdout / stderr 最好的办法之一 👍
下面给你一组 可以直接在 shell 里复制运行的实验,一步步“看见”2>&1 到底干了什么。
bashecho "这是 stdout" echo "这是 stderr" >&2
运行结果(终端上看):
text这是 stdout 这是 stderr
表面看一样,但它们来自不同 FD。
bashecho "这是 stdout" > out.txt echo "这是 stderr" >&2
结果:
这是 stderr
out.txt 内容:
这是 stdout
bashecho "这是 stdout" echo "这是 stderr" >&2 2> err.txt
结果:
这是 stdout
err.txt 内容:
这是 stderr
2>&1 到底是什么bashecho "这是 stdout" echo "这是 stderr" >&2
(还没合并,看不出差别)
bashecho "这是 stdout" echo "这是 stderr" >&2
换成:
bash{ echo "这是 stdout" echo "这是 stderr" >&2 } > all.txt
结果:
all.txt 内容:
这是 stdout
👉 因为 stderr 还没合并!
> all.txt 2>&1bash{ echo "这是 stdout" echo "这是 stderr" >&2 } > all.txt 2>&1
结果:
all.txt 内容:
text这是 stdout 这是 stderr
✅ stderr 被“接到” stdout 后,一起进文件
这是你最关心的 👇
bashOUT=$(echo "stdout"; echo "stderr" >&2) echo "OUT=[$OUT]"
结果:
textstderr OUT=[stdout]
👉 说明:
$(...) 捕获2>&1bashOUT=$(echo "stdout"; echo "stderr" >&2 2>&1) echo "OUT=[$OUT]"
⚠️ 这个写法是错的,看下面正确的 👇
bashOUT=$( echo "stdout" echo "stderr" >&2 2>&1 )
或者更清晰:
bashOUT=$( { echo "stdout" echo "stderr" >&2 } 2>&1 ) echo "OUT=[$OUT]"
结果:
textOUT=[stdout stderr]
✅ stdout + stderr 都被捕获了
bashOUT=$(ssh host "pgrep xxx" 2>/dev/null)
| 内容 | 去向 |
|---|---|
| pgrep PID | OUT |
| ssh 警告 | 丢弃 |
bashOUT=$(ssh host "pgrep xxx" 2>&1)
| 内容 | 去向 |
|---|---|
| PID | OUT |
| Warning / banner / 错误 | OUT(误判) |
$(...)只抓 stdout
2>&1是把 stderr “接”到 stdout 上
一接,就一起被抓
如果你愿意,下一步我可以教你一个调试万能招:
👉 用 exec 3>&1 4>&2 把 FD 拆出来“偷看”每一步的流向,这个在写复杂 shell 时非常有用。
[root@A06-R08-I202-51-3C0001L test_bigdata]# echo $(ssh 11.241.202.169 "pgrep xxx" 2>&1)
这样可以 吗? 我在命令行一行直接执行
可以执行,而且语法是对的 ✅
但我必须非常明确地告诉你一句:
⚠️ 这个写法只适合“临时调试看输出”,不适合用来做判断逻辑
下面我分层给你讲清楚 它“能不能用”、“实际发生了什么”、“有什么坑”。
bashecho $(ssh 11.241.202.169 "pgrep xxx" 2>&1)
$(...)bashssh 11.241.202.169 "pgrep xxx" 2>&1
ssh 的:
$(...) 捕获echo比如你想确认:
那么:
bashecho $(ssh 11.241.202.169 "pgrep xxx" 2>&1)
完全 OK 👍
echo $(...) 会 吃掉换行并重新分词举个例子:
bashssh host "printf '1\n2\n3\n'" 2>&1
原始输出是:
text1 2 3
但你写成:
bashecho $(ssh host "printf '1\n2\n3\n'" 2>&1)
实际变成:
1 2 3
原因:
$(...) → 命令替换$IFS(空格 / tab / 换行)重新分词bashssh 11.241.202.169 "pgrep xxx" 2>&1
这是 最干净、最推荐 的。
bashOUT=$(ssh 11.241.202.169 "pgrep xxx" 2>&1) printf 'OUT=[%s]\n' "$OUT"
或者(看不可见字符):
bashprintf 'OUT=%q\n' "$OUT"
因为:
bashecho $(ssh ... 2>&1)
不区分:
只要有任何文字,echo 就会打印 → 误判“有进程”
| 目的 | 推荐写法 |
|---|---|
| 临时看输出 | ssh host "pgrep xxx" 2>&1 |
| 看捕获内容 | OUT=$(ssh ... 2>&1); printf '%q\n' "$OUT" |
| 判断进程是否存在 | 不要用 2>&1 |
| 正式脚本 | 只抓 stdout + 校验 PID |
echo $(...)可以跑,但它会“改写”输出
调试可以,逻辑判断不行
如果你愿意,下一步我可以直接帮你写一个 “命令行一行版 + 脚本版”对照示例,让你在终端和脚本里都不踩坑。
ssh -o ConnectTimeout=20 -o StrictHostKeyChecking=no "PROCESS_NAME' > /dev/null"
这个输出 会是什么? 还是完全没有输出?
结论先给出来:
这条命令在“正常情况下”是完全没有任何输出的
(stdout 被丢弃,stderr 仍可能有输出)
下面把发生了什么、什么时候会有输出、常见误解一次讲清楚。
bashssh -o ConnectTimeout=20 -o StrictHostKeyChecking=no "$RANGER_HOST" \ "pgrep -f '$PROCESS_NAME' > /dev/null"
在 本地 shell 展开变量后,ssh 实际发送给远端的是类似:
bashpgrep -f 'xxx' > /dev/null
注意几点:
'$PROCESS_NAME'
> /dev/null
| 流 | 去向 |
|---|---|
| pgrep stdout (PID) | > /dev/null → 丢弃 |
| pgrep stderr | 没处理,仍然存在 |
| 来源 | 本地看到什么 |
|---|---|
| 远端 stdout | 没有(已被丢弃) |
| 远端 stderr | 仍然会显示 |
pgrep 无 stdout/dev/null 丢掉👉 终端完全没有任何输出
比如:
textssh: connect to host xxx port 22: Connection timed out
比如:
pgrep 不存在textbash: pgrep: command not found
bashssh host "pgrep -f 'xxx' > /dev/null" echo $?
0 → 找到进程1 → 没找到>1 → 出错这是 最干净、最专业 的用法之一 👍
那就要把 ssh 的 stderr 也干掉(在本地):
bashssh -o ConnectTimeout=20 -o StrictHostKeyChecking=no "$RANGER_HOST" \ "pgrep -f '$PROCESS_NAME' > /dev/null" \ 2>/dev/null
这样:
👉 绝对安静
bash"pgrep -f '$PROCESS_NAME'"
如果 PROCESS_NAME 里有单引号 ',会直接炸:
bashPROCESS_NAME="foo'bar"
远端命令会变成非法 shell 语法。
更稳的写法是:
bash"pgrep -f -- $(printf '%q' "$PROCESS_NAME") > /dev/null"
这条命令 不会有 stdout 输出
但 stderr 仍然可能出现
它最适合用来 只看 ssh 的退出码,而不是看输出
如果你愿意,我可以帮你把 **ssh + pgrep 的“最终标准模板”**整理成 3 行,直接在生产脚本里用,不再踩任何一个你刚才遇到的坑。
[root@A06-R08-I202-51-3C0001L test_bigdata]# ssh -o ConnectTimeout=20 -o StrictHostKeyChecking=no 11.241.202.169 "pgrep -f 'loop_ranger_server_test.sh' > /dev/null"
[root@A06-R08-I202-51-3C0001L test_bigdata]# echo $?
0
但是 loop_ranger_server_test.sh 和这个脚本 相关的进程就是不存在,为什么返回值还是0呢?
这个现象你观察得非常关键,而且你现在已经踩到了 pgrep -f 最容易被误判的一个坑。
结论先给出:
返回值是 0,说明
pgrep -f 'loop_ranger_server_test.sh'在远端确实匹配到了某个“命令行字符串”,但它不一定是你以为的那个进程。
下面我按确定性 → 原因 → 如何验证 → 正确修法来讲。
pgrep -f 会匹配 整个命令行不仅仅是你以为的“脚本进程本身”。
也就是说,只要 任意一个进程的命令行里包含:
loop_ranger_server_test.sh
pgrep -f 就会返回 0。
你执行的是:
bashssh host "pgrep -f 'loop_ranger_server_test.sh' > /dev/null"
在远端,会短暂存在一个类似这样的进程:
textbash -c pgrep -f 'loop_ranger_server_test.sh' > /dev/null
⚠️ 注意:
loop_ranger_server_test.sh 这个字符串,已经出现在这个 bash -c 的命令行里了!
于是:
bashpgrep -f 'loop_ranger_server_test.sh'
👉 匹配到的就是“它自己”
这就是为什么:
pgrep 仍然返回 0这是 pgrep -f 的经典陷阱。
bashssh 11.241.202.169 "pgrep -af 'loop_ranger_server_test.sh'"
你大概率会看到类似:
text12345 bash -c pgrep -f 'loop_ranger_server_test.sh' > /dev/null
如果看到这一行,说明我上面的判断 100% 命中。
因为这个进程:
ps -ef | grep 的时候它已经没了pgrep 是在它存在的瞬间执行的官方 man pgrep 里其实是暗示过的:
-fmatches against the full command line
而不是“进程名”。
bashssh host " pgrep -af 'loop_ranger_server_test.sh' | grep -v pgrep >/dev/null " echo $?
思路:
pgrep 自己如果这是一个脚本进程:
bashssh host " pgrep -af '[l]oop_ranger_server_test.sh' >/dev/null "
原理:
[l]oop... 不会出现在 pgrep 自己的命令行里这是 Unix 老技巧,非常好用 👍
-f,只匹配进程名(如果适用)bashssh host "pgrep loop_ranger_server_test.sh > /dev/null"
⚠️ 前提是:
bash xxx.shbashssh -o ConnectTimeout=20 -o StrictHostKeyChecking=no "$RANGER_HOST" \ "pgrep -af '[l]oop_ranger_server_test.sh' > /dev/null" RET=$? if [ $RET -eq 0 ]; then echo "PROCESS UP" else echo "PROCESS DOWN" fi
pgrep -f会把“包含字符串的任何命令行”都算进程
包括“检查它的那条命令自己”
你现在遇到的,不是 ssh 的问题,也不是重定向的问题,而是一个非常经典、非常隐蔽的 pgrep -f 自身匹配问题。
如果你愿意,我可以再帮你把你整个「ssh + pgrep + 状态判断」的脚本整理成一个生产级、安全、零误判版本。
[root@A06-R08-I202-51-3C0001L test_bigdata]# ssh 11.241.202.169 "pgrep -af 'loop_ranger_server_test.sh'"
[root@A06-R08-I202-51-3C0001L test_bigdata]# ssh 11.241.202.169 "pgrep -af 'loop_ranger_server_test.sh'"
但是这个还是没有值?
对,这个现象完全可能发生:你前面那条命令之所以 echo $? 得到 0,但你现在 pgrep -af 又查不到任何输出,最常见的解释是:
pgrep -f 匹配到了“远端执行这条命令的 shell 包装进程”,而不是目标进程本身ssh 远端执行时通常是类似这样跑的(概念上):
sh -c "pgrep -f 'loop_ranger_server_test.sh' > /dev/null"
这个 sh -c ... 的命令行里包含 loop_ranger_server_test.sh,所以:
pgrep -f 'loop_ranger_server_test.sh' 能匹配到这个 sh -c 自己/包装进程/dev/null 了,所以你看不到 PID 输出pgrep -af 'loop_ranger_server_test.sh' 时,pgrep 自身通常会避免匹配自己(或时序不同),就可能变成啥也打印不出来关键点:
-f是按“整条命令行”匹配的,包装进程的命令行也算。
你刚才只贴了输出,没有贴退出码。请直接这样看:
bashssh 11.241.202.169 "pgrep -af 'loop_ranger_server_test.sh'"; echo "exit=$?"
正常如果真没匹配到,应该是 exit=1。
[l] 技巧(强烈推荐)这样包装进程命令行里出现的是字面量 "[l]oop...",不会匹配真实字符串 loop...,但正则又能匹配真正进程命令行里的 loop...:
bashssh 11.241.202.169 "pgrep -af '[l]oop_ranger_server_test.sh'" echo "exit=$?"
如果确实不存在,就会:
exit=1如果存在,就会输出 PID 和命令行,exit=0。
bashssh 11.241.202.169 "pgrep -f '[l]oop_ranger_server_test.sh' >/dev/null" echo "exit=$?"
如果你把下面两条的输出/退出码贴一下,我能帮你 100% 定位是哪一种情况:
ssh ... "pgrep -af 'loop...'" ; echo exit=$?ssh ... "pgrep -af '[l]oop...'" ; echo exit=$?[root@A06-R08-I202-51-3C0001L test_bigdata]# pgrep -f "xxx"
[root@A06-R08-I202-51-3C0001L test_bigdata]# echo ?
1
[root@A06-R08-I202-51-3C0001L test_bigdata]# pgrep -f "xxx"
[root@A06-R08-I202-51-3C0001L test_bigdata]# echo "exit=?"
exit=1
[root@A06-R08-I202-51-3C0001L test_bigdata]# hostname -i
11.241.202.51
[root@A06-R08-I202-51-3C0001L test_bigdata]# ssh 11.241.202.51 "pgrep -f xxx"
80699
为什么?
因为你 ssh 到的是同一台机器(11.241.202.51),所以远端执行 pgrep -f xxx 时,机器上正好存在一个命令行里包含 xxx 的进程——通常就是你这条正在跑的 ssh 客户端命令本身(或远端 sh -c ... 的包装进程)。
你在 11.241.202.51 上敲:
bashssh 11.241.202.51 "pgrep -f xxx"
这会在这台机器上启动一个本地进程,命令行大概长这样:
bashssh 11.241.202.51 pgrep -f xxx
注意:这条命令行里就包含了 xxx。
而你 ssh 进去后运行的 pgrep -f xxx,是在同一台机器上做“全命令行匹配”(-f),所以它很可能匹配到的就是这个正在运行的 ssh 进程(或其子进程),于是返回了一个 PID:80699。
这也解释了:
pgrep -f "xxx" → 当时没有任何包含 xxx 的进程,所以 exit=1ssh ... "pgrep -f xxx" → 在远端执行瞬间,ssh 这条命令正在跑,所以匹配到,exit=0 且打印 PID在 11.241.202.51 上立刻跑:
bashps -fp 80699
或者更直观:
bashps -p 80699 -o pid,ppid,cmd
你大概率会看到 cmd 里包含 ssh ... pgrep -f xxx 或 sh -c pgrep -f xxx 一类。
[x]xx 技巧让 pgrep 的匹配模式写成正则 [x]xx,它能匹配目标 xxx,但不会出现在你自己命令行里(你写的是 [x]xx 不是 xxx):
bashssh 11.241.202.51 "pgrep -af '[x]xx'"
只做存在性判断:
bashssh 11.241.202.51 "pgrep -f '[x]xx' >/dev/null" echo $?
bashssh 11.241.202.51 "pgrep -af xxx | grep -v -E 'pgrep|ssh' "
同理,pgrep -f 'loop_ranger_server_test.sh' 也可能匹配到远端的 sh -c "pgrep -f 'loop...'" 包装命令行,导致 exit=0。
所以检测脚本时也建议用:
bashssh host "pgrep -f '[l]oop_ranger_server_test.sh' >/dev/null"
如果你把 ps -p 80699 -o pid,ppid,cmd 的输出贴一下,我可以帮你确认它到底是 ssh 进程还是远端包装 shell。