Java調(diào)用Linux系統(tǒng)命令
有時候,我們在使用Java做一些操作時,可能性能上并不能達(dá)到我們滿意的效果,就拿最近工作中的遇到的一個場景來說,需要對大量的小文件進(jìn)行合并成一個大文件。
最開始的想法是使用Java做文件操作,遍歷所有小文件然后往一個文件寫(可以做成并發(fā)寫),但是發(fā)現(xiàn)操作過程中遇到個問題,寫一千多個小文件在本機Windows下需要花費幾十秒的時間,即使在Linux環(huán)境下高配置的機器也需要將近十秒,這明顯對接口的響應(yīng)時間產(chǎn)生重要影響。這塊怎么優(yōu)化下呢?
我們都知道在Linux下可以進(jìn)行大文件的分割和合并,分別采用split和cat命令,于是做了個實驗,在Linux下對相同的一個1G文件進(jìn)行切割成1000個小文件,然后對這一千多個小文件進(jìn)行合并。效果是驚人的!!!竟然瞬間就能合成完成了!這更加讓我堅定了應(yīng)該使用系統(tǒng)命令進(jìn)行批量小文件進(jìn)行合并的想法。
我們這里封裝一個類,用來調(diào)用系統(tǒng)命令,然后得到系統(tǒng)調(diào)用的返回結(jié)果。
我們先封裝了一個返回結(jié)果類:
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
|
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)用實現(xiàn)類:
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
|
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類如下:
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
|
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(); } } } |
上面是一個簡單實現(xiàn),但是可能會存在一個問題,那就是執(zhí)行系統(tǒng)命令的時間如果本身比較長,如果不想一直等待到系統(tǒng)命令執(zhí)行完,而是在一段時間沒有返回就直接認(rèn)為失敗,所以需要增加過期時間的考慮。這里我借助于Future框架,將上面的調(diào)用系統(tǒng)命令的方法封裝成一個Callable對象。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
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,具體實現(xiàn)如下:
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
|
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; } } |
至此,我們在需要調(diào)用系統(tǒng)命令時直接調(diào)用CommandHelper.process(command)就可以了,然后拿到返回結(jié)果ProcessResult。我也是自己做個記錄,有需要的朋友可以直接拿去用。
順便說一句,采用封裝的這個類在完成上面相同的任務(wù)時,時間都在相同的機器上,耗時從原來的10s瞬間減少至200ms以內(nèi),由此可見,在適當(dāng)?shù)膱鼍罢{(diào)用系統(tǒng)命令是多么重要啊。
java執(zhí)行Linux命令,支持通配符(*)
java執(zhí)行l(wèi)inux或者windows命令,這個需求比較常見。
但是若使用 Runtime.getRuntime().exec(cmd); 會發(fā)現(xiàn),若cmd中含有通配符,則無法執(zhí)行,如cp /dira/*.txt /dirb
可用如下方式執(zhí)行:
1
2
3
4
5
|
String[] cmdArr = new String[ 3 ]; cmdArr[ 0 ] = "/bin/sh" ; cmdArr[ 1 ] = "-c" ; cmdArr[ 2 ] = command; process = Runtime.getRuntime().exec(cmdArr); |
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持服務(wù)器之家。
原文鏈接:https://majing.blog.csdn.net/article/details/88773993