0 問題發生
xiaojietest.java
package tasks; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.io.InputStreamReader; import java.io.Writer; import java.nio.file.Path; import java.nio.file.Paths; import java.sql.SQLException; import java.util.Iterator; import java.util.Map; import java.util.Set; import org.apache.commons.lang3.SystemUtils; import database.Tools; import util.FixPath; import util.StreamGobbler; public class xiaojietest { public static void main(String args[]) throws SQLException { try { String cmd="\""+"/home/xiaojie/Desktop/xiaojiework/BigCloneEval/sample/nicadRunner"+ "\" \"" + "/home/xiaojie/Desktop/xiaojiework/BigCloneEval/ijadataset/bcb_reduced/5"+"\""; System.out.println(cmd); String cmd1="bash"; //String cmd2="--help"; String cmd2="-c"; //String [] exec = {cmd1,cmd2}; //String [] exec = {"bash", "-c", "\""+"/home/xiaojie/Desktop/xiaojiework/BigCloneEval/commands/../sample/nicadRunner"+ " " + "/home/xiaojie/Desktop/xiaojiework/BigCloneEval/ijadataset/bcb_reduced/5"+"\""}; //String [] exec = {"bash", "--help"}; //String [] exec = {"bash", "-c",cmd}; String [] exec = {cmd1,cmd2,cmd}; //String[] exec= {"ls"}; ProcessBuilder pb = new ProcessBuilder(exec); //pb.directory(new File("/home/xiaojie/Desktop/xiaojiework/BigCloneEval/commands/../sample/")); Process p = pb.start(); Map<String, String>env=pb.environment(); //xiaojie output environment Set<String> key=env.keySet(); for(Iterator<String>it=key.iterator();it.hasNext();) { String s=it.next(); System.out.println(s+":"+env.get(s)); } //new StreamGobbler(p.getErrorStream()).start(); String line = null; BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream())); Path output=Paths.get("/home/xiaojie/Desktop/xiaojiework/data_for_experiment/nicadOutPutFile/nicad.clones"); output = FixPath.getAbsolutePath(output); output = output.toAbsolutePath(); BufferedWriter out = new BufferedWriter(new FileWriter(output.toFile())); //System.out.println(br.read()); while((line = br.readLine()) != null) { System.out.println(line); line = line.trim(); if(!line.equals("")) { out.write(line + "\n"); } } int retval = p.waitFor(); br.close(); System.out.println("retval:"+retval); } catch (Exception e) { e.printStackTrace(System.err); } } }
上述代碼期望通過Java程序執行如下腳本
/home/xiaojie/Desktop/xiaojiework/BigCloneEval/sample/nicadRunner
並且傳入參數:
/home/xiaojie/Desktop/xiaojiework/BigCloneEval/ijadataset/bcb_reduced/5
nicadRunner的腳本內容是:
#!/bin/bash # This tool runner works with the myconfig.cfg nicad configuration file included # You will need to modify the hard-coded installation below before running # Test this out on one of the IJaDataset directories (such as 11/) to test and # see that clones are detected and output in the correct format for BigCloneEval # as specified in the readme. ulimit -s hard root=`dirname $1` dir=`basename $1` path=$root/$dir # Go to NiCad installation directory cd /home/xiaojie/Desktop/xiaojiework/NiCad-5.0/ # Execute NiCad, Suppress Output ./nicad5 functions java "$path" myconfig > /dev/null 2> /dev/null # Convert Detected Clones Into BigCloneEval Format java -jar Convert.jar ${path}_functions-blind-abstract-clones/${dir}_functions-blind-abstract-clones-0.30.xml 2> /dev/null #cat ${path}_functions-blind-abstract-clones/${dir}_functions-blind-abstract-clones-0.30.xml | sed 's$<source file="$$g' | sed 's$" startline="$,$g' | sed 's$" endline="$,$g' | sed 's$" pcid=.*"></source>$$g' | sed 's$<clone nlines=.*$$g' | sed 's$</clone>.*$$g' | sed 's$</clones>$$g' |sed 's$<clones>$$g' | sed 's$<cloneinfo.*$$g' | sed 's$<systeminfo.*$$g' | sed 's$<runinfo.*$$g' | sed '/^$/d' | paste -d ',' - - | sed "s#${path}/##g" | sed 's#/#,#g' # Cleanup rm -rf ${path}_functions-blind-abstract-clones > /dev/null 2> /dev/null rm ${path}_functions-blind-abstract.xml > /dev/null 2> /dev/null rm ${path}_functions-clones*.log > /dev/null 2> /dev/null rm ${path}_functions-blind.xml > /dev/null 2> /dev/null rm ${path}_functions.xml > /dev/null 2> /dev/null
ProcessBuilder啟動進程並執行,正常的返回值(通過代碼中p.waitFor()返回)是0,其余狀態都說明進程執行過程報錯。
針對"ls"、"bash --help"等使用上面程序執行,都無錯誤。
但是針對如下進程使用上述程序通過ProcessBuilder啟動進程執行卻一直報錯:
bash -c "/home/xiaojie/Desktop/xiaojiework/BigCloneEval/sample/nicadRunner" "/home/xiaojie/Desktop/xiaojiework/BigCloneEval/ijadataset/bcb_reduced/5"
1 問題排查過程
1.1 bash -c的直接使用
首先,直接運行腳本,傳入參數。沒有任何錯誤。
其次,加上bash –c以后就會出錯。
這是因為必須將"/home/xiaojie/Desktop/xiaojiework/BigCloneEval/sample/nicadRunner" "/home/xiaojie/Desktop/xiaojiework/BigCloneEval/ijadataset/bcb_reduced/5"作為整體傳遞給bash -c,而不是分開。所以如下修改即可:
1.2 通過ProcessBuilder啟動進程執行bash -c
問題1:返回127錯誤碼
String cmd="\""+"/home/xiaojie/Desktop/xiaojiework/BigCloneEval/sample/nicadRunner"+ "\" \"" + "/home/xiaojie/Desktop/xiaojiework/BigCloneEval/ijadataset/bcb_reduced/5"+"\""; System.out.println(cmd); String cmd1="bash"; //String cmd2="--help"; String cmd2="-c"; String [] exec = {cmd1,cmd2,cmd}; ProcessBuilder pb = new ProcessBuilder(exec);
第一,如果將cmd寫作
"\"\""+"/home/xiaojie/Desktop/xiaojiework/BigCloneEval/sample/nicadRunner"+ "\" \"" + "/home/xiaojie/Desktop/xiaojiework/BigCloneEval/ijadataset/bcb_reduced/5"+"\"\"";
將該字符串打印以后會輸出:
""/home/xiaojie/Desktop/xiaojiework/BigCloneEval/sample/nicadRunner" "/home/xiaojie/Desktop/xiaojiework/BigCloneEval/ijadataset/bcb_reduced/5""
從表面看,是bash -c能夠接受的參數,即是一個整體。
這個時候,bash -c 將“”引號內作為一個整體看待,而不是一個腳本和一個參數,故而會提示127。
但是,對於ProcessBuilder而言,其接收該參數對其處理時,會將其當作最外層還有一層雙引號。就變成了
"""/home/xiaojie/Desktop/xiaojiework/BigCloneEval/sample/nicadRunner" "/home/xiaojie/Desktop/xiaojiework/BigCloneEval/ijadataset/bcb_reduced/5"""
會報出127錯誤。
shell的錯誤碼說明是:
因此,找不到nicadRunner腳本的路徑。即Path不對。
問題2:返回1錯誤碼
我們將問題1修正以后,即cmd為
String cmd="\""+"/home/xiaojie/Desktop/xiaojiework/BigCloneEval/sample/nicadRunner"+ "\" \"" + "/home/xiaojie/Desktop/xiaojiework/BigCloneEval/ijadataset/bcb_reduced/5"+"\"";
此時,執行前述程序后,返回1錯誤碼,即通用錯誤。
說明nicadRunner腳本運行了,但是沒有正確運行。
采取“逐步增加行”的策略。我們運行到./nicad5的那一行時出了錯誤。
#!/bin/bash # This tool runner works with the myconfig.cfg nicad configuration file included # You will need to modify the hard-coded installation below before running # Test this out on one of the IJaDataset directories (such as 11/) to test and # see that clones are detected and output in the correct format for BigCloneEval # as specified in the readme. ulimit -s hard root=`dirname $1` dir=`basename $1` path=$root/$dir # Go to NiCad installation directory cd /home/xiaojie/Desktop/xiaojiework/NiCad-5.0/ # Execute NiCad, Suppress Output ./nicad5 functions java "$path" myconfig > /dev/null 2> /dev/null
而直接在客戶端的命令行中用nicad命令執行,卻沒有錯誤。
為什么?
在客戶端的命令行中運行nicad程序,命令行中的上下文的path是包括FreeTXL路徑的,所以命令行nicad沒問題。
但是,使用ProcessBuilder啟動bash -c 運行nicadRunner腳本,腳本中再調用nicad程序的時候,就找不到FreeTXL路徑了!
我的FreeTXL路徑設置的比較特殊,是在一個私人文件夾,並且寫入的path是~/.bashrc。FreeTXL是nicad工具的依賴包。我安裝的時候,相關的路徑信息是:
Installing TXL for xiaojie only. Installing TXL commands into /home/xiaojie/bin Installing TXL library into /home/xiaojie/txl/lib Installing TXL manual entries into /home/xiaojie/txl/man/man1 Testing TXL
我在程序中添加代碼:
Map<String, String>env=pb.environment(); //xiaojie output environment Set<String> key=env.keySet(); for(Iterator<String>it=key.iterator();it.hasNext();) { String s=it.next(); System.out.println(s+":"+env.get(s)); }
獲取了ProcessBuilder啟動的命令的上下文,然后查看一下輸出
PATH:/home/xiaojie/Desktop/xiaojiework/jdk-8u191-linux-x64/jdk1.8.0_191/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin
可以看到,/home/xiaojie/bin不在PATH路徑中。
我當時將這個路徑寫入了一個bashrc文件,所以bash命令行中有該路徑!
但是,創建ProcessBuilder時,讀取的上下文,不是從bashrc中讀取的。
所以不一致,所以就會導致命令行和程序中的不一樣。
調試程序,跟入pb.environment();,可以看到:
進一步跟入,發現:
該函數只有一個聲明。
初步判斷是java.lang.ProcessEnvironment.environ函數。java.lang.ProcessEnvironment是java的一個類,源碼被隱藏。可以直接調用。
https://www.ibm.com/developerworks/cn/java/java-random-code-from-the-perspective-of-compilation/。該網頁中有Linux環境變量的讀取介紹,比較復雜。
https://www.cnblogs.com/sunilsun/p/6071124.html這里是Linux環境變量的介紹。
里面提到:
(1)~/.profile:【推薦】每個用戶都可使用該文件輸入專用於自己使用的shell信息,當用戶登錄時,該文件僅僅執行一次!默認情況下,他設置一些環境變量,執行用戶的.bashrc文件。這里是推薦放置個人設置的地方
(2)~/.bashrc:該文件包含專用於你的bash shell的bash信息,當登錄時以及每次打開新的shell時,該文件被讀取。不推薦放到這兒,因為每開一個shell,這個文件會讀取一次,效率肯定有影響。
所以,我將路徑改為寫入~/.profile。重啟,然后運行程序。解決。