通过SSH执行命令时,如何等待wget与tar完成后再执行后续操作?
我明白你的困扰——用PHP的SSH扩展执行命令时,明明需要等wget、tar这类耗时命令跑完再执行下一步,结果rm这类后续命令直接提前启动了。这种情况大多是因为你用的SSH库的exec方法默认没有等待当前命令执行完成就返回了,导致命令序列变成了并行启动,而不是串行执行。
下面给你几个实用的解决办法,亲测有效:
1. 把所有命令串成一个shell命令执行
最直接的方式是用&&把所有命令连起来,放到同一个exec调用里。&&的作用是只有前一个命令执行成功且完全结束后,才会执行下一个命令,而且整个序列会在同一个shell进程里按顺序跑:
$ssh->exec('mkdir -p /root/example && wget -q -P /root http://www.example.com/example.tar.bz2 && tar -xjf /root/example.tar.bz2 -C /root/example && rm /root/example.tar.bz2');
这里给你加了mkdir -p,就算/root/example已经存在,命令也不会报错,更健壮。
2. 让SSH库等待命令执行完成
如果你用的是phpseclib这类常用的SSH库,很多时候可以通过调用额外方法来强制等待当前命令执行结束。比如在phpseclib 3.x里,你可以用read()方法来等待命令输出结束,确保命令真的跑完了:
// 先建目录,等它完成 $ssh->exec('mkdir /root/example'); $ssh->read(); // 阻塞直到命令执行完毕 // 下载文件,等下载完成 $ssh->exec('wget -q -P /root http://www.example.com/example.tar.bz2'); $ssh->read(); // 解压文件,等解压完成 $ssh->exec('tar -xjf /root/example.tar.bz2 -C /root/example'); $ssh->read(); // 最后删除压缩包 $ssh->exec('rm /root/example.tar.bz2'); $ssh->read();
不同的SSH库可能有不同的等待方式,比如有些库提供waitForCommand()或者类似的方法,你可以去查下你用的库的文档,找对应的等待逻辑。
3. 用shell脚本封装所有步骤
如果你的命令序列以后可能要修改,或者逻辑比较复杂,不如把所有步骤写到一个shell脚本里,然后通过SSH执行这个脚本。shell本身会严格按顺序执行命令,完全不会出现提前跑的问题:
第一步,写一个setup_example.sh脚本:
#!/bin/bash # 创建目标目录,不存在就创建 mkdir -p /root/example # 下载压缩包 wget -q -P /root http://www.example.com/example.tar.bz2 # 解压到目标目录 tar -xjf /root/example.tar.bz2 -C /root/example # 删除压缩包 rm /root/example.tar.bz2
第二步,把这个脚本上传到远程服务器的某个目录(比如/root),然后用SSH执行:
# 先给脚本加执行权限,再运行 $ssh->exec('chmod +x /root/setup_example.sh && /root/setup_example.sh');
这种方式的好处是命令逻辑一目了然,以后要改步骤直接改脚本就行,不用动PHP代码。
内容的提问来源于stack exchange,提问作者Can

