Java調(diào)用shell命令涉及管道、重定向時不生效問題及解決
Java調(diào)用shell命令涉及管道、重定向時不生效
近日,因項目需求需要用java調(diào)用shell命令實現(xiàn)清理過時圖片任務,發(fā)現(xiàn)代碼生成出來的shell命令在linux系統(tǒng)后臺直接執(zhí)行,可以實現(xiàn)效果,但是,經(jīng)過java代碼運行,則達不到預期效果。
經(jīng)研究發(fā)現(xiàn),因為該shell命令涉及了管道,這情況就有點不一樣了,下面是針對Java調(diào)用shell命令涉及管道、重定向時不生效問題的解決方法
參考代碼如下:
public class Test
{
/**
* @param args
* @throws IOException
* @throws InterruptedException
*/
public static void main(String[] args) throws IOException, InterruptedException
{
Process p;
String command = "find /opt/Img/ \"2019-11-22-*.jpg\" | xargs rm -rf"};
// 必須加上sh -c
p = Runtime.getRuntime().exec(new String[]{"sh","-c",command});
if(0==p.waitFor())
{
System.out.println("Command execute result is OK!");
}
else
{
System.out.println("Command execute result is fail......");
}
}
}Java執(zhí)行shell遇到的各種問題
1、判斷子進程是否執(zhí)行結(jié)束
有的時候我們用java調(diào)用shell之后,之后的操作要在Process子進程正常執(zhí)行結(jié)束的情況下才可以繼續(xù),所以我們需要判斷Process進程什么時候終止。
Process類提供了waitFor()方法。該方法導致當前線程等待,直到Process線程終止。
Process.waitFor()是有一個int類型返回值的,當返回值為0的時候表Process進程正常終止。否則一般是腳本執(zhí)行出錯了(我遇到的一般是這種情況)。
2、Process.waitFor()導致當前線程阻塞
有的時候我們發(fā)現(xiàn)調(diào)用waitFor()方法后,java主線程會一直阻塞在waitFor()處,阻塞的原因是什么呢?分析一下:
Java在執(zhí)行Runtime.getRuntime().exec(jyName)之后,Linux會創(chuàng)建一個進程,該進程與JVM進程建立三個管道連接,標準輸入流、標準輸出流、標準錯誤流,假設linux進程不斷
向標準輸出流和標準錯誤流寫數(shù)據(jù),而JVM卻不讀取,數(shù)據(jù)會暫存在linux緩存區(qū),當緩存區(qū)存滿之后導致該進程無法繼續(xù)寫數(shù)據(jù),會僵死,導致java進程會卡死在waitFor()處,
永遠無法結(jié)束。
解決辦法:java進程在waitFor()前不斷讀取標準輸出流和標準錯誤流:
?? ??? ?//jyName ?解壓腳本路徑
?? ??? ?String fileName=fileList.get(0).toString().substring(fileList.get(0).toString().lastIndexOf(File.separator)+1);
?? ??? ?String ?jyName="/etc/zxvf.sh "+fileName;
?? ??? ?try {
?? ??? ??? ?Process p0 = Runtime.getRuntime().exec(jyName);
?? ??? ??? ?//讀取標準輸出流
?? ??? ??? ?BufferedReader bufferedReader =new BufferedReader(new InputStreamReader(p0.getInputStream()));
?? ??? ??? ?String line;
?? ??? ??? ?while ((line=bufferedReader.readLine()) != null) {
?? ??? ??? ? ? ?System.out.println(line);
?? ??? ??? ?}?? ?
?? ??? ??? ?//讀取標準錯誤流
?? ??? ??? ?BufferedReader brError = new BufferedReader(new InputStreamReader(p0.getErrorStream(), "gb2312"));
?? ??? ??? ?String errline = null;
?? ??? ??? ?while ((errline = brError.readLine()) != null) {
?? ??? ??? ??? ? System.out.println(errline);
?? ??? ??? ?}
?? ??? ??? ?//waitFor()判斷Process進程是否終止,通過返回值判斷是否正常終止。0代表正常終止
?? ??? ??? ?int c=p0.waitFor();
?? ??? ??? ?if(c!=0){
?? ??? ??? ??? ?baseRes.put("desc", "軟件升級失敗:執(zhí)行zxvf.sh異常終止");
?? ??? ??? ??? ?baseRes.setReturnFlag(false);
?? ??? ??? ??? ?return baseRes;
?? ??? ??? ?}
?? ??? ?} catch (IOException e1) {
?? ??? ??? ?baseRes.put("desc", "軟件升級失?。何募鈮菏?);
?? ??? ??? ?baseRes.setReturnFlag(false);
?? ??? ??? ?return baseRes;
?? ??? ?} catch (InterruptedException e1) {
?? ??? ??? ?baseRes.put("desc", "軟件升級失?。何募鈮菏?);
?? ??? ??? ?baseRes.setReturnFlag(false);
?? ??? ??? ?return baseRes;
?? ??? ?}也可以在執(zhí)行Runtime.getRuntime().exec(jyName)之后另外再啟動兩個線程分別讀取標準錯誤流和標準輸出流
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
?
public class ExcuteThread extends Thread {
?? ?private String name;
?
?? ?public ExcuteThread(String name) {
?? ??? ?this.name = name;
?? ?}
?? ?@Override
?? ?public void run() {
?? ??? ?try {
?? ??? ??? ?Process p = Runtime.getRuntime().exec(name);
?? ??? ??? ?InputStream fis = p.getInputStream();
?? ??? ??? ?final BufferedReader brError = new BufferedReader(
?? ??? ??? ??? ??? ?new InputStreamReader(p.getErrorStream(), "gb2312"));
?? ??? ??? ?InputStreamReader isr = new InputStreamReader(fis, "gb2312");
?? ??? ??? ?final BufferedReader br = new BufferedReader(isr);
?? ??? ??? ?Thread t1 = new Thread() {
?? ??? ??? ??? ?public void run() {
?? ??? ??? ??? ??? ?String line = null;
?? ??? ??? ??? ??? ?try {
?? ??? ??? ??? ??? ??? ?while ((line = brError.readLine()) != null) {
?? ??? ??? ??? ??? ??? ??? ?// System.out.println(line);
?? ??? ??? ??? ??? ??? ?}
?? ??? ??? ??? ??? ?} catch (IOException e) {
?? ??? ??? ??? ??? ??? ?e.printStackTrace();
?? ??? ??? ??? ??? ?} finally {
?? ??? ??? ??? ??? ??? ?try {
?? ??? ??? ??? ??? ??? ??? ?if (brError != null)
?? ??? ??? ??? ??? ??? ??? ??? ?brError.close();
?? ??? ??? ??? ??? ??? ?} catch (IOException e) {
?? ??? ??? ??? ??? ??? ??? ?e.printStackTrace();
?? ??? ??? ??? ??? ??? ?}
?? ??? ??? ??? ??? ?}
?? ??? ??? ??? ?}
?? ??? ??? ?};
?? ??? ??? ?Thread t2 = new Thread() {
?? ??? ??? ??? ?public void run() {
?? ??? ??? ??? ??? ?String line = null;
?? ??? ??? ??? ??? ?try {
?? ??? ??? ??? ??? ??? ?while ((line = br.readLine()) != null) {
?? ??? ??? ??? ??? ??? ??? ?// System.out.println(line);
?? ??? ??? ??? ??? ??? ?}
?? ??? ??? ??? ??? ?} catch (IOException e) {
?? ??? ??? ??? ??? ??? ?e.printStackTrace();
?? ??? ??? ??? ??? ?} finally {
?? ??? ??? ??? ??? ??? ?try {
?? ??? ??? ??? ??? ??? ??? ?if (br != null)
?? ??? ??? ??? ??? ??? ??? ??? ?br.close();
?? ??? ??? ??? ??? ??? ?} catch (IOException e) {
?? ??? ??? ??? ??? ??? ??? ?// TODO Auto-generated catch block
?? ??? ??? ??? ??? ??? ??? ?e.printStackTrace();
?? ??? ??? ??? ??? ??? ?}
?? ??? ??? ??? ??? ?}
?? ??? ??? ??? ?}
?? ??? ??? ?};
?? ??? ??? ?t1.start();
?? ??? ??? ?t2.start();
?
?? ??? ?} catch (IOException e1) {
?? ??? ??? ?// TODO Auto-generated catch block
?? ??? ??? ?e1.printStackTrace();
?? ??? ?} finally {
?? ??? ?}
?
?? ?}
?
}3、shell腳本中有關聯(lián)腳本,注意路徑
就是shell腳本中還要執(zhí)行其他腳本,這時候就是注意一個路徑的問題,這個問題也是我找了好長時間的一個問題。
Process p=Runtime.getRuntime().exec(“/etc/a.sh”)
在Test.java類調(diào)用了etc目錄下的a.sh腳本, a.sh腳本中執(zhí)行etc目錄下的b.sh腳本,原來我在a.sh腳本中寫的是./b.sh。其實這樣linux是找不到b.sh的,因為我們執(zhí)行是在
Test.class目錄下調(diào)用的/etc/a.sh 所以當a.sh中執(zhí)行./b.sh的時候他會在Test.class目錄下尋找,所以找不到,所以a.sh中要寫成/etc/b.sh
4、java連續(xù)調(diào)用多個腳本
String[] cmd = { "/bin/sh", "-c", "rm -rf /installation/upgrade/ ; mkdir /installation/upgrade/" };
Process p = Runtime.getRuntime().exec(cmd);
p.waitFor();就是這種數(shù)組的方式。
5、java執(zhí)行.sh腳本文件的時候直接寫目錄就行
例如這樣:Runtime.getRuntime().exec(“/etc/a.sh”)
java 直接執(zhí)行語句的時候需要加上"/bin/sh" 例如這樣:
String name="/bin/sh cd /installation/upgrade/ip89_install_packet"; Process p = Runtime.getRuntime().exec(name);
總結(jié)
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關文章
Spring Boot + Thymeleaf + Activiti 快速開發(fā)平臺項目 附源碼
這篇文章主要介紹了Spring Boot + Thymeleaf + Activiti 快速開發(fā)平臺項目附源碼,代碼簡單易懂,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-04-04
詳解IDEA中類加載器調(diào)用getResourceAsStream()方法需注意的問題
這篇文章主要介紹了詳解IDEA中類加載器調(diào)用getResourceAsStream()方法需注意的問題,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2021-02-02
一個applicationContext 加載錯誤導致的阻塞問題及解決方法
這篇文章主要介紹了一個applicationContext 加載錯誤導致的阻塞問題及解決方法,需要的朋友可以參考下2018-11-11
Java實現(xiàn)拖拽文件上傳dropzone.js的簡單使用示例代碼
本篇文章主要介紹了Java實現(xiàn)拖拽文件上傳dropzone.js的簡單使用示例代碼,具有一定的參考價值,有興趣的可以了解一下2017-07-07
性能爆棚的實體轉(zhuǎn)換復制工具MapStruct使用詳解
這篇文章主要為大家介紹了性能爆棚的實體轉(zhuǎn)換復制工具MapStruct使用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-03-03

