java如何執(zhí)行l(wèi)inux命令
前言
java 執(zhí)行l(wèi)inux 命令;
本文模擬復(fù)制linux文件到指定文件夾后打zip包后返回zip名稱,提供給下載接口下載zip;
一、linux命令執(zhí)行
linux命令執(zhí)行
Process process = Runtime.getRuntime().exec
或
Process process = new ProcessBuilder(commands).start();
/** * 執(zhí)行Linux命令 */ public static void execCommand(String commands) throws IOException { Process process = null; BufferedReader reader = null; try { //創(chuàng)建進(jìn)程實(shí)例執(zhí)行Linux命令 process = Runtime.getRuntime().exec(new String[]{"/bin/sh", "-c", commands}); // 獲取標(biāo)準(zhǔn)輸入流 reader = new BufferedReader(new InputStreamReader(process.getInputStream())); String line; while ((line = reader.readLine()) != null) { //log.info("linux----" + line); } // 等待進(jìn)程結(jié)束 int exitCode = process.waitFor(); if (exitCode != 0) { log.error("執(zhí)行Linux命令異常,exitCode={},linux command={}", exitCode, commands); throw new BusinessCheckException("執(zhí)行Linux命令異常"); } } catch (IOException | InterruptedException e) { log.error("執(zhí)行Linux命令異常", e); throw new BusinessCheckException("執(zhí)行Linux命令異常"); } finally { if (reader != null) { reader.close(); } if (process != null) { process.destroy(); } } }
二、使用步驟
1.通過linux命令
打包目標(biāo)數(shù)據(jù)到zip
public String downToZip(CorpQrCodeReq req, HttpServletResponse response) { StopWatch sw = new StopWatch(); // 參數(shù)校驗(yàn) if (CollectionUtils.isEmpty(req.getCorpIds()) || CollectionUtils.isEmpty(req.getCorpNames())) { return "0"; } // 生成保存目錄文件夾 String uuid = UUID.randomUUID().toString().replace("-", ""); String baseUploadPath = "/tmp/zip/"+uuid+"/"; FileUtil.mkdir(baseUploadPath); try { //生成二維碼到臨時(shí)目錄 List<String> enterpriseIds = req.getCorpIds(); List<String> enterpriseNames = req.getCorpNames(); List<String> command = new ArrayList<>(enterpriseIds.size()); // 拼接被復(fù)制的文件地址 for (int i = 0; i < enterpriseIds.size(); i++) { String path = this.qrCode(CorpQrCodeReq.builder() .corpIds(Lists.newArrayList(enterpriseIds.get(i))) .corpNames(Lists.newArrayList(enterpriseNames.get(i))).build()); command.add(path + " "); } sw.stop(); log.info(sw.getLastTaskName() + sw.getTotalTimeSeconds() + "s"); sw.start("執(zhí)行cp命令"); String property = System.getProperty("line.separator"); //拼接shell腳本 String sb = " for i in " + String.join(" ", command) + property + "do" + property + " cp -n \"$i\" " + baseUploadPath + property + "done"; //執(zhí)行cp命令 execCommand(sb); sw.stop(); log.info(sw.getLastTaskName() + sw.getTotalTimeSeconds() + "s"); sw.start("執(zhí)行壓縮命令"); //執(zhí)行壓縮命令 統(tǒng)一壓縮比一個(gè)一個(gè)壓縮進(jìn)去效率高 //-j忽略源文件路徑,直接打包目標(biāo)文件 String zipCommand = " /bin/zip -1 -j " + baseUploadPath.concat(".zip") + " " + baseUploadPath + "/*"; execCommand(zipCommand); sw.stop(); log.info(sw.getLastTaskName() + sw.getTotalTimeSeconds() + "s"); log.info(sw.prettyPrint()); return uuid; } catch (Exception e) { log.error("壓縮包下載失敗 ", e); throw new BusinessCheckException("壓縮包下載失敗"); } }
2.根據(jù)uuid
下載zip文件(2個(gè)接口,一個(gè)是根據(jù)條件生成zip,另一個(gè)根據(jù)zip名稱下載)
此處下載應(yīng)用了nginx的X-Accel-Redirect
具體使用方法參考springboot X-Accel-Redirect 大文件下載實(shí)現(xiàn)
public void downZip(String path, HttpServletResponse response) { try { String baseUploadPath ="/tmp/zip/"; response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode("企業(yè)二維碼", StandardCharsets.UTF_8.toString()) + ".zip"); //設(shè)置URI給nginx進(jìn)行內(nèi)部的跳轉(zhuǎn) response.setHeader("X-Accel-Redirect", "/upload" + baseUploadPath.concat(path).concat(".zip")); } catch (Exception e) { log.error(" 二維碼壓縮包下載失敗 ", e); throw new BusinessCheckException("二維碼壓縮包下載失敗"); } }
三、踩坑
1.linux 和 java 2個(gè)進(jìn)程異步問題
linux命令執(zhí)行后,和java服務(wù)是2個(gè)進(jìn)程。
當(dāng)linux命令執(zhí)行過程期間,java下面的業(yè)務(wù)服務(wù)已觸發(fā)時(shí)(比如文件數(shù)量較大,而下載接口觸發(fā)較快時(shí))會(huì)造成數(shù)據(jù)不完整(zip包打包不全,一般一個(gè)文件沒有)。
此時(shí)我們需要利用 讀取執(zhí)行流 + process.waitFor()
,等待linux進(jìn)程結(jié)束后再做業(yè)務(wù)處理。
2.關(guān)于Process初始化
Process process = null; // 如果commands拼接的命令中包含空格 會(huì)自動(dòng)識(shí)別為2段命令 process = new ProcessBuilder(commands).start(); // 不支持空格和| process = Runtime.getRuntime().exec(commands); // -c 表示cmd是一條命令,不會(huì)被截?cái)? process = Runtime.getRuntime().exec(new String[]{"/bin/sh", "-c", commands});
3.linux for cp 空格識(shí)別問題
執(zhí)行腳本如下:
for i in "/data/mnt/www/corp-qr/769847/九臺(tái)區(qū)九臺(tái)中由 生活服務(wù)部.jpg" do cp -n $i /data/zip/5e0e67f59c524a4e9851abd3a3dfe0ac done
此時(shí)執(zhí)行命令報(bào)錯(cuò):
cp: 無法獲取"/data/mnt/www/corp-qr/769847/九臺(tái)區(qū)九臺(tái)中由" 的文件狀態(tài)(stat): 沒有那個(gè)文件或目錄 cp: 無法獲取"生活服務(wù)部.jpg" 的文件狀態(tài)(stat): 沒有那個(gè)文件或目錄
linux 會(huì)將目標(biāo)文件解析成2個(gè)文件。
解決方案:
"/data/mnt/www/corp-qr/769847/九臺(tái)區(qū)九臺(tái)中由 生活服務(wù)部.jpg" 修改為 "/data/mnt/www/corp-qr/769847/*" 或 cp -n $i /data/zip/5e0e67f59c524a4e9851abd3a3dfe0ac 修改為 cp -n "$i" /data/zip/5e0e67f59c524a4e9851abd3a3dfe0ac
5.執(zhí)行效率優(yōu)化
一千個(gè)文件測(cè)試結(jié)果:
- a. 文件循環(huán)時(shí)直接打包到zip
"/bin/zip -rj " + baseUploadPath.concat(".zip") + " " + baseUploadPath + "/*";
效率不如cp
到文件夾下壓縮文件夾 - b. 調(diào)整壓縮率
zip -1
- c. 文件循環(huán)時(shí)直接cp,由于
execCommand
執(zhí)行l(wèi)inux命令要等返回結(jié)果再執(zhí)行,所以效率也不高
6.cp 文件覆蓋問題
同名文件cp linux會(huì)提示是否覆蓋,如果通過java執(zhí)行cp,無法給予是否覆蓋的回應(yīng),報(bào)錯(cuò):沒有那個(gè)文件或目錄,如果忽略覆蓋提示?
覆蓋提示原因:
我們執(zhí)行cp
時(shí) 實(shí)際linux執(zhí)行的是cp -i
,-i
表示覆蓋前提示
-- 命令前加反斜線忽略alias \cp /var/tmp/test.txt /tmp -- 使用命令全路徑 /bin/cp /var/tmp/test.txt /tmp -- 先取消別名再復(fù)制(不推薦) unalias cp -- 不覆蓋 cp -n /var/tmp/test.txt /tmp
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Java+opencv3.2.0實(shí)現(xiàn)人臉檢測(cè)功能
這篇文章主要為大家詳細(xì)介紹了Java+opencv3.2.0實(shí)現(xiàn)人臉檢測(cè)功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-02-02Java實(shí)現(xiàn)Kruskal算法的示例代碼
Kruskal算法是一種用來尋找最小生成樹的算法,由Joseph Kruskal在1956年發(fā)表。用來解決同樣問題的還有Prim算法和Boruvka算法等。本文將介紹用Java語言實(shí)現(xiàn)Kruskal算法的示例代碼,需要的可以參考一下2022-07-07深入解析Java的Hibernate框架中的持久對(duì)象
Hibernate的持久對(duì)象在數(shù)據(jù)庫數(shù)據(jù)操作中有著重要作用,這里我們就來深入解析Java的Hibernate框架中的持久對(duì)象,首先必須從理解持久化對(duì)象的生命周期開始:2016-07-07Java RabbitMQ 中的消息長(zhǎng)期不消費(fèi)會(huì)過期嗎
RabbitMQ支持消息的過期時(shí)間,在消息發(fā)送時(shí)可以進(jìn)行指定。 RabbitMQ支持隊(duì)列的過期時(shí)間,從消息入隊(duì)列開始計(jì)算,只要超過了隊(duì)列的超時(shí)時(shí)間配置,那么消息會(huì)自動(dòng)的清除2021-09-09springboot項(xiàng)目docker分層構(gòu)建的配置方式
在使用dockerfile構(gòu)建springboot項(xiàng)目時(shí),速度較慢,用時(shí)比較長(zhǎng),為了加快構(gòu)建docker鏡像的速度,采用分層構(gòu)建的方式,這篇文章主要介紹了springboot項(xiàng)目docker分層構(gòu)建,需要的朋友可以參考下2024-03-03