亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Java程序怎么去調用并執行shell腳本?

發布時間:2020-06-23 10:50:33 來源:億速云 閱讀:325 作者:清晨 欄目:編程語言

這篇文章將為大家詳細講解有關Java程序怎么去調用并執行shell腳本?,小編覺得挺實用的,因此分享給大家做個參考,希望大家閱讀完這篇文章后可以有所收獲。

我們在開發過程中,大部分是java開發, 而在文本處理過程中,主要就是腳本進行開發。 java開發的特點就是我們可以很早地進行TDDL, METAQ 等等地對接; 而腳本開發的特點就是在進行批處理的時候非常方便。

背景

我們在開發過程中,大部分是java開發, 而在文本處理過程中,主要就是腳本進行開發。 java開發的特點就是我們可以很早地進行TDDL, METAQ 等等地對接; 而腳本開發的特點就是在進行批處理的時候非常方便。 前陣子我遇到這么一個需求場景: 對抓取的數據進行打包, 后來又遇到我要通過腳本進行抓取,比如nodejs下基于phantomjs的casperjs爬蟲。

解決方法

對于第一個問題:java抓取,并且把結果打包。
那么比較直接的做法就是,java接收各種消息(db,metaq等等),然后借助于jstorm集群進行調度和抓取。 最后把抓取的結果保存到一個文件中,并且通過調用shell打包, 回傳。 也許有同學會問, 為什么不直接把java調用odps直接保存文件,答案是,我們的集群不是hz集群,直接上傳odps速度很有問題,因此先打包比較合適。(這里不糾結設計了,我們回到正題)

java調用shell的方法

通過ProcessBuilder進行調度

這種方法比較直觀,而且參數的設置也比較方便, 比如我在實踐中的代碼(我隱藏了部分業務代碼):

ProcessBuilder pb = new ProcessBuilder("./" + RUNNING_SHELL_FILE, param1,
            param2, param3);
  pb.directory(new File(SHELL_FILE_DIR));
  int runningStatus = 0;
  String s = null;
  try {
   Process p = pb.start();
   try {
    runningStatus = p.waitFor();
   } catch (InterruptedException e) {
   }
 
  } catch (IOException e) {
  }
  if (runningStatus != 0) {
  }
  return;

這里有必要解釋一下幾個參數:

RUNNING_SHELL_FILE:要運行的腳本
SHELL_FILE_DIR:要運行的腳本所在的目錄; 當然你也可以把要運行的腳本寫成全路徑。
runningStatus:運行狀態,0標識正常。 詳細可以看java文檔。
param1, param2, param3:可以在RUNNING_SHELL_FILE腳本中直接通過1,2,$3分別拿到的參數。

直接通過系統Runtime執行shell

這個方法比較暴力,也比較常用, 代碼如下:

p = Runtime.getRuntime().exec(SHELL_FILE_DIR + RUNNING_SHELL_FILE + " "+param1+" "+param2+" "+param3);
p.waitFor();

我們發現,通過Runtime的方式并沒有builder那么方便,特別是參數方面,必須自己加空格分開,因為exec會把整個字符串作為shell運行。

可能存在的問題以及解決方法

如果你覺得通過上面就能滿足你的需求,那么可能是要碰壁了。你會遇到以下情況。

沒權限運行

這個情況我們團隊的朱東方就遇到了, 在做DTS遷移的過程中,要執行包里面的shell腳本, 解壓出來了之后,發現執行不了。 那么就按照上面的方法授權吧

ProcessBuilder builder = new ProcessBuilder("/bin/chmod", "755", tempFile.getPath());
   Process process = builder.start();
   int rc = process.waitFor();

java進行一直等待shell返回

這個問題估計更加經常遇到。 原因是, shell腳本中有echo或者print輸出, 導致緩沖區被用完了! 為了避免這種情況, 一定要把緩沖區讀一下, 好處就是,可以對shell的具體運行狀態進行log出來。 比如上面我的例子中我會變成:

ProcessBuilder pb = new ProcessBuilder("./" + RUNNING_SHELL_FILE, keyword.trim(),
            taskId.toString(), fileName);
  pb.directory(new File(CASPERJS_FILE_DIR));
  int runningStatus = 0;
  String s = null;
  try {
   Process p = pb.start();
   BufferedReader stdInput = new BufferedReader(new InputStreamReader(p.getInputStream()));
   BufferedReader stdError = new BufferedReader(new InputStreamReader(p.getErrorStream()));
   while ((s = stdInput.readLine()) != null) {
    LOG.error(s);
   }
   while ((s = stdError.readLine()) != null) {
    LOG.error(s);
   }
   try {
    runningStatus = p.waitFor();
   } catch (InterruptedException e) {
   } 

記得在start()之后, waitFor()之前把緩沖區讀出來打log, 就可以看到你的shell為什么會沒有按照預期運行。 這個還有一個好處是,可以讀shell里面輸出的結果, 方便java代碼進一步操作。

也許你還會遇到這個問題,明明手工可以運行的命令,java調用的shell中某一些命令居然不能執行,報錯:命令不存在!

比如我在使用casperjs的時候,手工去執行shell明明是可以執行的,但是java調用的時候,發現總是出錯。 通過讀取緩沖區就能發現錯誤日志了。 我發現即便自己把安裝的casperjs的bin已經加入了path中(/etc/profile, 各種bashrc中)還不夠。 比如:

export NODE_HOME="/home/admin/node"
export CASPERJS_HOME="/home/admin/casperjs"
export PHANTOMJS_HOME="/home/admin/phantomjs"
export PATH=$PATH:$JAVA_HOME/bin:/root/bin:$NODE_HOME/bin:$CASPERJS_HOME/bin:$PHANTOMJS_HOME/bin

原來是因為java在調用shell的時候,默認用的是系統的/bin/下的指令。特別是你用root權限運行的時候。 這時候,你要在/bin下加軟鏈了。針對我上面的例子,就要在/bin下加軟鏈:

ln -s /home/admin/casperjs/bin/casperjs casperjs;
ln -s /home/admin/node/bin/node node;
ln -s /home/admin/phantomjs/bin/phantomjs phantomjs;

這樣,問題就可以解決了。

如果是通過java調用shell進行打包,那么要注意路徑的問題了

因為shell里面tar的壓縮和解壓可不能直接寫:

tar -zcf /home/admin/data/result.tar.gz /home/admin/data/result

直接給你報錯,因為tar的壓縮源必須到路徑下面, 因此可以寫成

tar -zcf /home/admin/data/result.tar.gz -C /home/admin/data/ result

如果我的shell是在jar包中怎么辦?

答案是:解壓出來。再按照上面指示進行操作。

(1)找到路徑

String jarPath = findClassJarPath(ClassLoaderUtil.class);
  JarFile topLevelJarFile = null;
  try {
   topLevelJarFile = new JarFile(jarPath);
   Enumeration<JarEntry> entries = topLevelJarFile.entries();
   while (entries.hasMoreElements()) {
    JarEntry entry = entries.nextElement();
    if (!entry.isDirectory() && entry.getName().endsWith(".sh")) {
     對你的shell文件進行處理
    }
   }

對文件處理的方法就簡單了,直接touch一個臨時文件,然后把數據流寫入,代碼:

FileUtils.touch(tempjline);
tempjline.deleteOnExit();
FileOutputStream fos = new FileOutputStream(tempjline);
IOUtils.copy(ClassLoaderUtil.class.getResourceAsStream(r), fos);
fos.close();

有這個這個東東,相信大家會減少踩坑,而且大膽地使用java和腳本之間的交互吧。 java可以調用shell,那么shell再調用其他就方便了。 記得一點, 不要過度地依賴緩沖區進行線程之間的通信。原因自己去學習吧。

關于Java程序怎么去調用并執行shell腳本?就分享到這里了,希望以上內容可以對大家有一定的幫助,可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

霍城县| 施秉县| 淮南市| 临夏市| 泽州县| 太湖县| 新巴尔虎左旗| 嘉荫县| 安西县| 福泉市| 长岛县| 健康| 桐庐县| 松阳县| 长海县| 衡东县| 垣曲县| 黄山市| 呼玛县| 灌云县| 鞍山市| 连江县| 延寿县| 满洲里市| 西乌珠穆沁旗| 永川市| 桐城市| 阿尔山市| 桃园县| 平安县| 岳阳县| 旌德县| 澜沧| 江陵县| 高碑店市| 安顺市| 苏州市| 留坝县| 田林县| 辛集市| 临清市|