前言
上周接了个任务,写一个monitor定时采集服务器上指定的几个应用的数据,包括像CPU占用率、线程数、句柄数等等。不是很难,但是要下发的shell命令很多,采集的数据也很多,而且为了方便本地调试,就准备写两套代码。一套用于本地远程登录服务器下发shell,一套用于当monitor程序部署到服务器上本地执行shell。本篇就来讲一讲我在服务器本地执行shell遇到的坑。。。
Java要想下发shell命令怎么做呢?
- 远程:这个调用比较简单,要用到第三方库jcraft来做ssh连接,然后下发shell命令用Expect4j比较方便,但是最终返回出来的数据不太好处理,一大串字符串,我要的数据就那么几个,用正则来慢慢匹配只有。远程下发shell的网上资料很多,我现有的代码与公司业务结合比较紧密,不方便贴出来,可自定搜索。
- 本地:本地执行shell可以用jdk自带的Runtime和ProcessBuilder,推荐使用ProcessBuilder。
Runtime
eg:
1 | String s = "ps -ef|grep abc"; |
Runtime下面其实也是调用的ProcessBuilder,所以我推荐直接使用ProcessBuilder算了。。。
Runtime里面用到ProcessBuilder
ProcessBuilder
ProcessBuilder相对于Runtime传参要复杂一些,都是以数组或列表的形式。
eg:
1 | List<String> commmands = new ArrayList<>(); |
但是要注意了,这样会出问题。因为ProcessBuilder要求我们如果要执行的命令带有参数,我们需要把命令拆分开来,一个一个的放到数组里面才能正常执行,但是这样很不友好呀,我一个命令很长很长的时候怎办呢?这个时候可以用到sh -c。
我们可以man一下sh的用法,sh -c 后面跟字符串,这样就可以放入一个完整的命令啦。
所以修改一下代码
1 | List<String> commmands = new ArrayList<>(); |
然后再执行发现返回结果里面有grep命令本身的进程信息,所以过滤掉,修改命令
1 | ps aux | grep abc => ps aux | grep abc | grep -v grep |
当然如果和我一样用的windows系统,不好调试的话可以参考我之前文章