如何使用Java調(diào)用Linux系統(tǒng)命令
Java調(diào)用Linux系統(tǒng)命令
有時(shí)候,我們?cè)谑褂肑ava做一些操作時(shí),可能性能上并不能達(dá)到我們滿(mǎn)意的效果,就拿最近工作中的遇到的一個(gè)場(chǎng)景來(lái)說(shuō),需要對(duì)大量的小文件進(jìn)行合并成一個(gè)大文件。
最開(kāi)始的想法是使用Java做文件操作,遍歷所有小文件然后往一個(gè)文件寫(xiě)(可以做成并發(fā)寫(xiě)),但是發(fā)現(xiàn)操作過(guò)程中遇到個(gè)問(wèn)題,寫(xiě)一千多個(gè)小文件在本機(jī)Windows下需要花費(fèi)幾十秒的時(shí)間,即使在Linux環(huán)境下高配置的機(jī)器也需要將近十秒,這明顯對(duì)接口的響應(yīng)時(shí)間產(chǎn)生重要影響。這塊怎么優(yōu)化下呢?
我們都知道在Linux下可以進(jìn)行大文件的分割和合并,分別采用split和cat命令,于是做了個(gè)實(shí)驗(yàn),在Linux下對(duì)相同的一個(gè)1G文件進(jìn)行切割成1000個(gè)小文件,然后對(duì)這一千多個(gè)小文件進(jìn)行合并。效果是驚人的!?。【谷凰查g就能合成完成了!這更加讓我堅(jiān)定了應(yīng)該使用系統(tǒng)命令進(jìn)行批量小文件進(jìn)行合并的想法。
我們這里封裝一個(gè)類(lèi),用來(lái)調(diào)用系統(tǒng)命令,然后得到系統(tǒng)調(diào)用的返回結(jié)果。
我們先封裝了一個(gè)返回結(jié)果類(lèi):
package com.majing.learning.fileupload.common.process; public class ProcessResult { private boolean success = false; private String errorMessage; private String outputMessage; public boolean isSuccess() { return success; } public void setSuccess(boolean success) { this.success = success; } public String getErrorMessage() { return errorMessage; } public void setErrorMessage(String errorMessage) { this.errorMessage = errorMessage; } public String getOutputMessage() { return outputMessage; } public void setOutputMessage(String outputMessage) { this.outputMessage = outputMessage; } }
接著我們給出封裝的系統(tǒng)調(diào)用實(shí)現(xiàn)類(lèi):
package com.majing.learning.fileupload.common.process; import java.io.IOException; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import org.apache.commons.lang3.StringUtils; public class CommandUtils { public static ProcessResult runCmdTest(ExecutorService executorService, String command) throws IOException, InterruptedException { StringBuilder queryInputResult = new StringBuilder(); StringBuilder queryErroInputResult = new StringBuilder(); ProcessResult processResult = new ProcessResult(); String[] cmd = { "/bin/sh", "-c", command}; Process pro = Runtime.getRuntime().exec(cmd); CountDownLatch lock = new CountDownLatch(2); executorService.submit(new ProcessCheckTask(queryInputResult, lock, pro.getInputStream())); executorService.submit(new ProcessCheckTask(queryErroInputResult, lock, pro.getErrorStream())); boolean done = false; while (!done) { lock.await(); done = true; } processResult.setOutputMessage(queryInputResult.toString()); processResult.setErrorMessage(queryErroInputResult.toString()); processResult.setSuccess(StringUtils.isBlank(processResult.getErrorMessage())); return processResult; } }
其中ProcessCheckTask類(lèi)如下:
package com.majing.learning.fileupload.common.process; import java.io.BufferedReader; import java.io.InputStream; import java.io.InputStreamReader; import java.util.concurrent.CountDownLatch; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.majing.learning.fileupload.common.ConstValues; public class ProcessCheckTask implements Runnable { private static Logger logger = LoggerFactory.getLogger(ProcessCheckTask.class); /** 鎖 */ private CountDownLatch lock; /** 執(zhí)行結(jié)果輸入流 */ private InputStream inputStream; /** 字符拼接 */ private StringBuilder queryInputResult; public ProcessCheckTask(StringBuilder queryInputResult, CountDownLatch lock, InputStream inputStream) { super(); this.lock = lock; this.inputStream = inputStream; this.queryInputResult = queryInputResult; } @Override public void run() { try { BufferedReader bf = new BufferedReader(new InputStreamReader(inputStream)); String line = null; while ((line = bf.readLine()) != null && line.length() > 0) { queryInputResult.append(line).append("\n"); } } catch (Exception e) { logger.error(ConstValues.EXCEPTION_OCCURED, e); } finally { lock.countDown(); } } }
上面是一個(gè)簡(jiǎn)單實(shí)現(xiàn),但是可能會(huì)存在一個(gè)問(wèn)題,那就是執(zhí)行系統(tǒng)命令的時(shí)間如果本身比較長(zhǎng),如果不想一直等待到系統(tǒng)命令執(zhí)行完,而是在一段時(shí)間沒(méi)有返回就直接認(rèn)為失敗,所以需要增加過(guò)期時(shí)間的考慮。這里我借助于Future框架,將上面的調(diào)用系統(tǒng)命令的方法封裝成一個(gè)Callable對(duì)象。
package com.majing.learning.fileupload.common.process; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; public class CommandTask implements Callable<ProcessResult>{ private ExecutorService executorService; private String command; public CommandTask(ExecutorService executorService, String command){ this.executorService = executorService; this.command = command; } @Override public ProcessResult call() throws Exception { return CommandUtils.runCmdTest(executorService, command); } }
然后在上面的CommandUtils的基礎(chǔ)上再封裝一層變成CommandHelper,具體實(shí)現(xiàn)如下:
package com.majing.learning.fileupload.common.process; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.majing.learning.fileupload.common.ConstValues; public class CommandHelper { private static Logger logger = LoggerFactory.getLogger(CommandHelper.class); private static ExecutorService executorService=Executors.newFixedThreadPool(50); private static long default_timeout = 8000; public static ProcessResult process(String command){ return process(command, default_timeout, TimeUnit.MILLISECONDS); } public static ProcessResult process(String command, long timeout, TimeUnit unit){ CommandTask commandTask = new CommandTask(executorService, command); Future<ProcessResult> processResult = executorService.submit(commandTask); ProcessResult result = null; try{ result = processResult.get(timeout, unit); }catch(Exception e){ logger.error(ConstValues.EXCEPTION_OCCURED, e); } return result; } }
至此,我們?cè)谛枰{(diào)用系統(tǒng)命令時(shí)直接調(diào)用CommandHelper.process(command)就可以了,然后拿到返回結(jié)果ProcessResult。我也是自己做個(gè)記錄,有需要的朋友可以直接拿去用。
順便說(shuō)一句,采用封裝的這個(gè)類(lèi)在完成上面相同的任務(wù)時(shí),時(shí)間都在相同的機(jī)器上,耗時(shí)從原來(lái)的10s瞬間減少至200ms以?xún)?nèi),由此可見(jiàn),在適當(dāng)?shù)膱?chǎng)景調(diào)用系統(tǒng)命令是多么重要啊。
java執(zhí)行Linux命令,支持通配符(*)
java執(zhí)行l(wèi)inux或者windows命令,這個(gè)需求比較常見(jiàn)。
但是若使用 Runtime.getRuntime().exec(cmd); 會(huì)發(fā)現(xiàn),若cmd中含有通配符,則無(wú)法執(zhí)行,如cp /dira/*.txt /dirb
可用如下方式執(zhí)行:
String[] cmdArr = new String[3]; cmdArr[0] = "/bin/sh"; cmdArr[1] = "-c"; cmdArr[2] = command; process = Runtime.getRuntime().exec(cmdArr);
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
java 數(shù)據(jù)結(jié)構(gòu)中棧結(jié)構(gòu)應(yīng)用的兩個(gè)實(shí)例
這篇文章主要介紹了java 數(shù)據(jù)結(jié)構(gòu)中棧結(jié)構(gòu)應(yīng)用的兩個(gè)實(shí)例的相關(guān)資料,需要的朋友可以參考下2017-06-06基于Spring Boot的Environment源碼理解實(shí)現(xiàn)分散配置詳解
這篇文章主要給大家介紹了基于Spring Boot的Environment源碼理解實(shí)現(xiàn)分散配置的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2018-08-08SpringBoot編譯target目錄下沒(méi)有resource下的文件踩坑記錄
這篇文章主要介紹了SpringBoot編譯target目錄下沒(méi)有resource下的文件踩坑記錄,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-08-08淺談Servlet開(kāi)發(fā)技術(shù)基礎(chǔ)
這篇文章主要介紹了淺談Servlet開(kāi)發(fā)技術(shù)基礎(chǔ),具有一定借鑒價(jià)值,需要的朋友可以參考下。2017-12-12sftp和ftp 根據(jù)配置遠(yuǎn)程服務(wù)器地址下載文件到當(dāng)前服務(wù)
這篇文章主要介紹了sftp和ftp 根據(jù)配置遠(yuǎn)程服務(wù)器地址下載文件到當(dāng)前服務(wù)的相關(guān)資料本文給大家介紹的非常詳細(xì),具有參考借鑒價(jià)值,需要的朋友可以參考下2016-10-10Java AQS中CyclicBarrier回環(huán)柵欄的使用
這篇文章主要介紹了Java中的 CyclicBarrier詳解,CyclicBarrier沒(méi)有顯示繼承哪個(gè)父類(lèi)或者實(shí)現(xiàn)哪個(gè)父接口, 所有AQS和重入鎖不是通過(guò)繼承實(shí)現(xiàn)的,而是通過(guò)組合實(shí)現(xiàn)的,下文相關(guān)內(nèi)容需要的小伙伴可以參考一下2023-02-02