欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java基于命令行調(diào)用Python腳本的方法詳解

 更新時間:2025年06月10日 08:45:16   作者:千千寰宇  
這篇文章主要為大家詳細介紹了Java如何基于命令行實現(xiàn)調(diào)用Python腳本的方法,文中的示例代碼講解詳細,感興趣的小伙伴可以跟隨小編一起學習一下

需求描述

利用 Java 基于命令行調(diào)用 Python

環(huán)境信息

  • 基于 Ubuntu 24 的 Docker 容器
  • Python 3.12
  • Java 17

實現(xiàn)步驟

安裝 Python + PIP 環(huán)境

以基于 Ubuntu 24 的 Docker 環(huán)境為例

Dockerfile

# OS: Ubuntu 24.04
FROM swr.cn-north-4.myhuaweicloud.com/xxx/eclipse-temurin:17-noble
 
COPY ./target/*.jar /app.jar
COPY ./target/classes/xxx/ /xxx/

# install : python + pip (前置操作: 更新 apt 源)
RUN sed -i 's#http[s]*://[^/]*#http://mirrors.aliyun.com#g' /etc/apt/sources.list \
  && apt-get update \
  && apt-get -y install vim \
  && apt-get -y install --no-install-recommends python3 python3-pip python3-venv \
  && python3 -m venv $HOME/.venv \
  && . $HOME/.venv/bin/activate \ # 注:Linux 中 高版本 Python (3.5以上),必須在虛擬環(huán)境下方可正常安裝所需依賴包
  && pip install -i https://mirrors.aliyun.com/pypi/simple/ can cantools
#   && echo "alias python=python3" >> ~/.bashrc \ # Java程序的子進程調(diào)用中試驗:未此行命令未生效;但開發(fā)者獨自登錄 docker 容器內(nèi),有生效
#   && echo '. $HOME/.venv/bin/activate' >> ~/.bashrc \ # Java程序的子進程調(diào)用中試驗:未此行命令未生效;但開發(fā)者獨自登錄 docker 容器內(nèi),有生效
#   && echo 'export PYTHON=$HOME/.venv/bin/python' >> /etc/profile \ # Java程序的子進程調(diào)用中試驗:未此行命令未生效;但開發(fā)者獨自登錄 docker 容器內(nèi),有生效

#  && echo '. /etc/profile' > $HOME/app.sh \ # Java程序的子進程調(diào)用中試驗:未測通,有衍生問題未解決掉
#  && echo 'java ${JAVA_OPTS:-} -jar app.jar > /dev/null 2>&1 &' >> $HOME/app.sh \  # Java程序的子進程調(diào)用中試驗:未測通,有衍生問題未解決掉
#  && echo 'java ${JAVA_OPTS:-} -jar app.jar' >> $HOME/app.sh \  # Java程序的子進程調(diào)用中試驗:未測通,有衍生問題未解決掉
#  && chmod +x $HOME/app.sh \  # Java程序的子進程調(diào)用中試驗:未測通,有衍生問題未解決掉
#  && chown 777 $HOME/app.sh  # Java程序的子進程調(diào)用中試驗:未測通,有衍生問題未解決掉
 
EXPOSE 8080

# ENTRYPOINT exec sh $HOME/app.sh # Java程序的子進程調(diào)用中試驗:未測通,有衍生問題未解決掉
ENTRYPOINT exec java ${JAVA_OPTS:-} -DPYTHON=$HOME/.venv/bin/python -jar app.jar # 通過 Java 獲取 JVM 參數(shù)( System.getProperty("PYTHON") ) 方式獲取 【 Python 可執(zhí)行文件的絕對路徑】的值

編寫和準備 Python 業(yè)務腳本

  • step1 編寫 Python 業(yè)務腳本 (略)
  • step2 如果 Python 腳本在 JAVA 工程內(nèi)部(JAR包內(nèi)),則需在 執(zhí)行 Python 腳本前,將其提前拷貝為一份新的腳本文件到指定位置。
public XXX {
    private static String scriptFilePath;
    public static String TMP_DIR = "/tmp/xxx-sdk/";
	
	static {
        prepareHandleScript( TMP_DIR );
	}
 
    /**
     *  準備腳本文件到目標路徑
     * @note 無法直接執(zhí)行 jar 包內(nèi)的腳本文件,需要拷貝出來。
     * @param targetScriptDirectory 目標腳本的文件夾路徑
     *     而非腳本文件路徑 eg: "/tmp/xxx-sdk"
     */
    @SneakyThrows
    public static void prepareHandleScript(String targetScriptDirectory){
        File file = new File(targetScriptDirectory);
        //如果目標目錄不存在,則創(chuàng)建該目錄
        if (!file.exists() && !file.isDirectory()) {
            file.mkdirs();
        }
        File targetScriptFile = new File(targetScriptDirectory + "/xxx-converter.py");// targetScriptFile = "\tmp\xxx-sdk\xxx-converter.py"
        scriptFilePath = targetScriptFile.getAbsolutePath(); // scriptFilePath = "D:\tmp\xxx-sdk\xxx-converter.py"
 
        URL resource = CanAscLogGenerator.class
            .getClassLoader()
            .getResource( "bin/xxx-converter.py");
 
        InputStream converterPythonScriptInputStream = null;
        try {
            converterPythonScriptInputStream = resource.openStream();
            FileUtils.copyInputStreamToFile( converterPythonScriptInputStream, targetScriptFile );
        } catch (IOException exception){
            log.error("Fail to prepare the script!targetScriptDirectory:{}, exception:", targetScriptDirectory, exception);
            throw new RuntimeException(exception);
        } finally {
            if(converterPythonScriptInputStream != null){
                converterPythonScriptInputStream.close();
            }
        }
    }
}

Java 調(diào)用 Python 腳本

關鍵點:程序阻塞問題

程序阻塞問題

  • 通過 Process實例.getInputStream() 和 Process實例.getErrorStream() 獲取的輸入流錯誤信息流緩沖池向當前Java程序提供的,而不是直接獲取外部程序的標準輸出流和標準錯誤流。
  • 緩沖池的容量是一定的。

因此,若外部程序在運行過程中不斷向緩沖池輸出內(nèi)容,當緩沖池填滿,那么: 外部程序暫停運行直到緩沖池有空位可接收外部程序的輸出內(nèi)容為止。(
注:采用xcopy命令復制大量文件時將會出現(xiàn)該問題

解決辦法: 當前的Java程序不斷讀取緩沖池的內(nèi)容,從而為騰出緩沖池的空間。

Runtime r = Runtime.getRuntime();
try {
    Process proc = r.exec("cmd /c dir"); // 假設該操作為造成大量內(nèi)容輸出
  	// 采用字符流讀取緩沖池內(nèi)容,騰出空間
  	BufferedReader reader = new BufferedReader(new InputStreamReader(proc.getInputStream(), "gbk")));
  	String line = null;
  	while ((line = reader.readLine()) != null){
  	   System.out.println(line);
  	}
 	
  	/* 或采用字節(jié)流讀取緩沖池內(nèi)容,騰出空間
  	 ByteArrayOutputStream pool = new ByteArrayOutputStream();
  	 byte[] buffer = new byte[1024];
  	 int count = -1;
  	 while ((count = proc.getInputStream().read(buffer)) != -1){
  	   pool.write(buffer, 0, count);
  	   buffer = new byte[1024];
  	 }
  	 System.out.println(pool.toString("gbk"));
  	 */
 	
  	int exitVal = proc.waitFor();
  	System.out.println(exitVal == 0 ? "成功" : "失敗");
} catch(Exception e){
  	e.printStackTrace();
}

注意:外部程序在執(zhí)行結束后需自動關閉;否則,不管是字符流還是字節(jié)流均由于既讀不到數(shù)據(jù),又讀不到流結束符,從而出現(xiàn)阻塞Java進程運行的情況。

cmd的參數(shù) “/c” 表示當命令執(zhí)行完成后關閉自身

關鍵點: Java Runtime.exec() 方法

基本方法: Runtime.exec()

首先,在Linux系統(tǒng)下,使用Java調(diào)用Python腳本,傳入?yún)?shù),需要使用Runtime.exec()方法

即 在java中使用shell命令

這個方法有兩種使用形式:

方式1 無參數(shù)傳入 ,直接執(zhí)行Linux相關命令: Process process = Runtime.getRuntime().exec(String cmd);

無參數(shù)可以直接傳入字符串,如果需要傳參數(shù),就要用方式2的字符串數(shù)組實現(xiàn)。

方式2 有參數(shù)傳入,并執(zhí)行Linux命令: Process process = Runtime.getRuntime().exec(String[] cmd);

執(zhí)行結果

使用exec方法執(zhí)行命令,如果需要執(zhí)行的結果,用如下方式得到:

	String line;
    while ((line = processInputStream.readLine()) != null) { // InputStream processInputStream = process.getInputStream();
    	System.out.println(line);
         if ("".equals(line)) {
               break;
          }
    }
    System.out.println("line ----> " + line);

查看錯誤信息

	BufferedReader errorResultReader = new BufferedReader(new InputStreamReader(process.getErrorStream()));
	String errorLine;
	while ((errorLine = shellErrorResultReader.readLine()) != null) {
    	System.out.println("errorStream:" + errorLine);
    }
    int exitCode = process.waitFor();
    System.out.println("exitCode:" + exitCode);

簡單示例

	String result = "";
	String[] cmd = new String [] { "pwd" };
	Process process = Runtime.getRuntime().exec(cmd);
	InputStreamReader inputStreamReader = new InputStreamReader(process.getInputStream());
	LineNumberReader input = new LineNumberReader(inputStreamReader);
	result = input.readLine();
	System.out.println("result:" + result);

關鍵點: python 絕對路徑

查看(虛擬環(huán)境的)python 可執(zhí)行程序路徑,然后在Java調(diào)用的時候?qū)懗鼋^對路徑。

如:$HOME/.venv/bin/python以解決 Linux 環(huán)境中的 Python 3.X 的虛擬環(huán)境異常問題(pip install XXX : error: externally-managed-environment)。

Cannot run program “python“: error=2, No such file or director (因虛擬環(huán)境問題,找不到python命令和pip安裝的包)

Java 調(diào)用 Python 的實現(xiàn) (必讀)

@Slf4j
public class XxxxGenerator implements IGenerator<XxxxSequenceDto> {
    //python jvm 變量 (`-DPYTHON=$HOME/.venv/bin/python`)
    public static String PYTHON_VM_PARAM = "PYTHON";//System.getProperty(PYTHON_VM_PARAM)
    //python 環(huán)境變量名稱 //eg: "export PYTHON=$HOME/.venv/bin/python" , pythonEnv="$HOME/.venv/bin/python"
    public static String PYTHON_ENV_PARAM = "PYTHON";//;System.getenv(PYTHON_ENV_PARAM);
    private static String PYTHON_COMMAND ;
    //默認的 python 命令
    private static String PYTHON_COMMAND_DEFAULT = "python";
	
	//...
 
    static {
        PYTHON_COMMAND = loadPythonCommand();
        log.info("PYTHON_COMMAND:{}, PYTHON_VM:{}, PYTHON_ENV:{}", PYTHON_COMMAND, System.getProperty(PYTHON_VM_PARAM), System.getenv(PYTHON_ENV_PARAM) );
		
		//...
    }
 
    /**
     * 加載 python 命令的可執(zhí)行程序的路徑
     * @note
     *   Linux 中,尤其是 高版本 Python(3.x) ,為避免 Java 通過 `Runtime.getRuntime().exec(args)` 方式 調(diào)用 Python 命令時,報找不到 可執(zhí)行程序(`Python` 命令)\
     *   ————建議: java 程序中使用的 `python` 命令的可執(zhí)行程序路徑,使用【絕對路徑】
     * @return
     */
    private static String loadPythonCommand(){
        String pythonVm = System.getProperty(PYTHON_VM_PARAM);
        String pythonEnv = System.getenv(PYTHON_ENV_PARAM);
        String pythonCommand = pythonVm != null?pythonVm : pythonEnv;
        pythonCommand = pythonCommand != null?pythonCommand : PYTHON_COMMAND_DEFAULT;
        return pythonCommand;
    }
 
 
    /**
     * 業(yè)務方法: CAN ASC LOG 轉(zhuǎn) BLF
     * @param ascLogFilePath
     * @param blfFilePath
     */
    protected void convertToBlf(File ascLogFilePath, File blfFilePath){
        //CanAsclogBlfConverterScriptPath = "/D:/Workspace/CodeRepositories/xxx-platform/xxx-sdk/xxx-sdk-java/target/classes/bin/can-asclog-blf-converter.py"
        //String CanAsclogBlfConverterScriptPath = CanAscLogGenerator.class.getClassLoader().getResource("bin/can-asclog-blf-converter.py").getPath();
 
        String canAscLogBlfConverterScriptPath = XxxxGenerator.scriptFilePath;//python 業(yè)務腳本的文件路徑, eg: "D:\tmp\xxx-sdk\can-asclog-blf-converter.py"
 
        //String [] args = new String [] {"python", "..\\bin\\can-asclog-blf-converter.py", "-i", ascLogFilePath, "-o", blfFilePath};// ascLogFilePath="/tmp/xxx-sdk/can-1.asc" , blfFilePath="/tmp/xxx-sdk/can-1.blf"
        String [] args = new String [] { PYTHON_COMMAND, canAscLogBlfConverterScriptPath, "-i", ascLogFilePath.getPath(), "-o", blfFilePath.getPath()};
        log.info("args: {} {} {} {} {} {}", args);
        Process process = null;
        Long startTime = System.currentTimeMillis();
        try {
            process = Runtime.getRuntime().exec(args);
            Long endTime = System.currentTimeMillis();
            log.info("Success to convert can asc log file to blf file!ascLogFile:{}, blfFile:{}, timeConsuming:{}ms, pid:{}", ascLogFilePath, blfFilePath, endTime - startTime, process.pid());
        } catch (IOException exception) {
            log.error("Fail to convert can asc log file to blf file!ascLogFile:{}, blfFile:{}, exception:", ascLogFilePath, blfFilePath,  exception);
            throw new RuntimeException(exception);
        }
 
        //讀取 python 腳本的標準輸出
        // ---- input stream ----
        List<String> processOutputs = new ArrayList<>();
        try(
            InputStream processInputStream = process.getInputStream();
            BufferedReader processReader = new BufferedReader( new InputStreamReader( processInputStream ));
        ) {
            Long readProcessStartTime = System.currentTimeMillis();
            String processLine = null;
            while( (processLine = processReader.readLine()) != null ) {
                processOutputs.add( processLine );
            }
            process.waitFor();
            Long readProcessEndTime = System.currentTimeMillis();
            log.info("Success to read the can asc log to blf file's process standard output!timeConsuming:{}ms", readProcessEndTime - readProcessStartTime );
            log.info("processOutputs(System.out):{}", JSON.toJSONString( processOutputs ));
        } catch (IOException exception) {
            log.error("Fail to get input stream!IOException:", exception);
            throw new RuntimeException(exception);
        } catch (InterruptedException exception) {
            log.error("Fail to wait for the process!InterruptedException:{}", exception);
            throw new RuntimeException(exception);
        }
 
        // ---- error stream ----
        List<String> processErrors = new ArrayList<>();
        try(
            InputStream processInputStream = process.getErrorStream();
            BufferedReader processReader = new BufferedReader( new InputStreamReader( processInputStream ));
        ) {
            Long readProcessStartTime = System.currentTimeMillis();
            String processLine = null;
            while( (processLine = processReader.readLine()) != null ) {
                processErrors.add( processLine );
            }
            process.waitFor();
            Long readProcessEndTime = System.currentTimeMillis();
            log.error("Success to read the can asc log to blf file's process standard output!timeConsuming:{}ms", readProcessEndTime - readProcessStartTime );
            log.error("processOutputs(System.err):{}", JSON.toJSONString( processOutputs ));
        } catch (IOException exception) {
            log.error("Fail to get input stream!IOException:", exception);
            throw new RuntimeException(exception);
        } catch (InterruptedException exception) {
            log.error("Fail to wait for the process!InterruptedException:{}", exception);
            throw new RuntimeException(exception);
        }
        if( processErrors.size() > 0 ) {
            throw new RuntimeException( "convert to blf failed!\nerrors:" + JSON.toJSONString(processErrors) );
        }
    }
}

到此這篇關于Java基于命令行調(diào)用Python腳本的方法詳解的文章就介紹到這了,更多相關Java調(diào)用Python腳本內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • java中的Io(input與output)操作總結(二)

    java中的Io(input與output)操作總結(二)

    這一節(jié)我們來討論關于文件自身的操作包括:創(chuàng)建文件對象、創(chuàng)建和刪除文件、文件的判斷和測試、創(chuàng)建目錄、獲取文件信息、列出文件系統(tǒng)的根目錄、列出目錄下的所有文件,等等,感興趣的朋友可以了解下
    2013-01-01
  • java jdk動態(tài)代理詳解

    java jdk動態(tài)代理詳解

    動態(tài)代理類的Class實例是怎么生成的呢,是通過ProxyGenerator類來生成動態(tài)代理類的class字節(jié)流,把它載入方法區(qū)
    2013-09-09
  • Spring IO Platform簡單介紹

    Spring IO Platform簡單介紹

    這篇文章主要介紹了Spring IO Platform簡單介紹,具有一定借鑒價值,需要的朋友可以參考下
    2017-12-12
  • 詳解Spring配置文件中bean的相關屬性

    詳解Spring配置文件中bean的相關屬性

    這篇文章主要為大家詳細介紹了Spring配置文件中bean的相關屬性的知識,文中的示例代碼講解詳細,感興趣的小伙伴可以跟隨小編一起學習一下
    2024-12-12
  • ElasticSearch學習之Es索引Api操作

    ElasticSearch學習之Es索引Api操作

    這篇文章主要為大家介紹了ElasticSearch學習之Es索引Api操作詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-01-01
  • Java實現(xiàn)HttpGet請求傳body參數(shù)

    Java實現(xiàn)HttpGet請求傳body參數(shù)

    這篇文章主要為大家詳細介紹了Java實現(xiàn)HttpGet請求傳body參數(shù)的相關知識,文中的示例代碼講解詳細,感興趣的小伙伴可以跟隨小編一起學習一下
    2024-02-02
  • 將Java項目提交到云服務器的流程步驟

    將Java項目提交到云服務器的流程步驟

    所謂將項目提交到云服務器即將你的項目打成一個 jar 包然后提交到云服務器即可,因此我們需要準備服務器環(huán)境為:Linux + JDK + MariDB(MySQL)+ Git + Maven,文中通過圖文講解的非常詳細,需要的朋友可以參考下
    2025-04-04
  • 深入理解Java設計模式之觀察者模式

    深入理解Java設計模式之觀察者模式

    這篇文章主要介紹了JAVA設計模式之觀察者模式的的相關資料,文中示例代碼非常詳細,供大家參考和學習,感興趣的朋友可以了解
    2021-11-11
  • 基于XML配置Spring的自動裝配過程解析

    基于XML配置Spring的自動裝配過程解析

    這篇文章主要介紹了基于XML配置Spring的自動裝配過程解析,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2019-10-10
  • Springboot實現(xiàn)郵箱驗證代碼實例

    Springboot實現(xiàn)郵箱驗證代碼實例

    這篇文章主要介紹了Springboot實現(xiàn)郵箱驗證代碼實例,在一些業(yè)務需求中我們經(jīng)常需要使用郵箱進行驗證碼的收取,本文通過簡單的代碼實例來說明,需要的朋友可以參考下
    2024-01-01

最新評論