背景
java可以通過Runtime來調用其他進程,如cmd命令,shell文件或腳本等。
基本用法
Runtime執行時返回一個Process對象,利用該對象完成腳本執行。下面的例子中,Linux的/home/目錄下有一個刪除指定日期文件的腳本deletefile.sh,Java調用該腳本的方法如下。
/**
* 刪除指定日期的文件
* @param date yyyy-MM-dd格式
*/
private static void setSystemDate(String date){
Process process = null;
String command1 = "/bin/sh /home/deletefile.sh "+date;
System.out.println(command1);
try {
process = Runtime.getRuntime().exec(command1);
//必須等待該進程結束,否則時間設置就無法生效
process.waitFor();
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}finally{
if(process!=null){
process.destroy();
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
緩沖區滿問題
如果腳本執行過程中產生大量的控制台輸出信息,這種信息會被Shell進程輸出到內存緩沖區中,而上述用法中作為父進程的java進程並沒有處理緩沖區內容,那么就會出現緩沖區滿,Java進程掛起的情況。
解決辦法是,使用Java線程讀取Shell腳本的輸出信息。
public static List<String> executeShell(String shpath, String var){
//String shpath="/test/test.sh"; // sh 路徑
//String var="201102"; // sh 參數
String shellVar = (var==null)?"":var;
String command1 = "chmod 777 " + shpath; // 為 sh 添加權限
String command2 = "/bin/sh " + shpath + " " + shellVar;
final List<String> strList = new ArrayList<String>();
Process process1 = null;
BufferedReader in = null;
try {
process1 = Runtime.getRuntime().exec(command1); // 執行添加權限的命令
process1.waitFor(); // 如果執行多個命令,必須加上
final Process process2 = Runtime.getRuntime().exec(command2);
//處理InputStream的線程
new Thread() {
@Override
public void run() {
BufferedReader in = new BufferedReader(new InputStreamReader(process2.getInputStream()));
String line = null;
try{
while((line = in.readLine()) != null) {
strList.add(line);
}
}catch(IOException e) {
e.printStackTrace();
}finally {
try {
in.close();
}
catch (IOException e) {
e.printStackTrace();
}
}
}
}.start();
//處理ErrorStream的線程
new Thread() {
@Override
public void run(){
BufferedReader err = new BufferedReader(new InputStreamReader(process2.getErrorStream()));
String line = null;
try{
while((line = err.readLine()) != null)
{
strList.add(line);
}
}catch(IOException e){
e.printStackTrace();
}finally{
try{
err.close();
}catch (IOException e){
e.printStackTrace();
}
}
}
}.start();
process2.waitFor();
} catch (IOException e) {
} catch (InterruptedException e) {
}finally {
if(in != null){
try {
in.close();
} catch (IOException e) {
}
}
if(process1 != null){
process1.destroy();
}
}
return strList;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
線程阻塞問題
如果執行的shell腳本中有等待輸入命令,比如more操作,那么在使用java進程讀取緩沖區內容的時候,會出現線程阻塞等待的問題。
例如,下面的腳本讀取文件內容。
filePath=/home/software.info
softType=$(more $filePath)
echo $softType
- 1
- 2
- 3
上述三行代碼很簡單地輸出了一個文件的內容,而且這個文件很短,只有一行,在Linux下執行沒有問題,但是如果用Java調用executeShell方法執行的話,處理InputStream的線程會出現阻塞等待問題,根源在於腳本中的more命令,它會等待控制台的輸入,從而導致了Java進程的阻塞。
解決辦法就是避免在shell腳本中使用more命令,用cat命令替換即可。
管道命令不執行問題
使用Process執行shell命令時,如果命令中包含管道命令,直接執行會得不到正確結果,解決辦法是使用/bin/sh,將/bin/sh放在待執行的shell命令的前面,可保證管道命令的正確執行。
String command1 = "/bin/sh ifconfig | grep if= | awk '{print $1,$6}'";
- 1
- 2
啟示錄
究竟在Java中調用Shell是不是明智的呢?