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

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

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

需求描述

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

環(huán)境信息

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

實(shí)現(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程序的子進(jìn)程調(diào)用中試驗(yàn):未此行命令未生效;但開發(fā)者獨(dú)自登錄 docker 容器內(nèi),有生效
#   && echo '. $HOME/.venv/bin/activate' >> ~/.bashrc \ # Java程序的子進(jìn)程調(diào)用中試驗(yàn):未此行命令未生效;但開發(fā)者獨(dú)自登錄 docker 容器內(nèi),有生效
#   && echo 'export PYTHON=$HOME/.venv/bin/python' >> /etc/profile \ # Java程序的子進(jìn)程調(diào)用中試驗(yàn):未此行命令未生效;但開發(fā)者獨(dú)自登錄 docker 容器內(nèi),有生效

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

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

編寫和準(zhǔn)備 Python 業(yè)務(wù)腳本

  • step1 編寫 Python 業(yè)務(wù)腳本 (略)
  • 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 );
	}
 
    /**
     *  準(zhǔn)備腳本文件到目標(biāo)路徑
     * @note 無法直接執(zhí)行 jar 包內(nèi)的腳本文件,需要拷貝出來。
     * @param targetScriptDirectory 目標(biāo)腳本的文件夾路徑
     *     而非腳本文件路徑 eg: "/tmp/xxx-sdk"
     */
    @SneakyThrows
    public static void prepareHandleScript(String targetScriptDirectory){
        File file = new File(targetScriptDirectory);
        //如果目標(biāo)目錄不存在,則創(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 腳本

關(guān)鍵點(diǎn):程序阻塞問題

程序阻塞問題

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

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

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

Runtime r = Runtime.getRuntime();
try {
    Process proc = r.exec("cmd /c dir"); // 假設(shè)該操作為造成大量?jī)?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é)束后需自動(dòng)關(guān)閉;否則,不管是字符流還是字節(jié)流均由于既讀不到數(shù)據(jù),又讀不到流結(jié)束符,從而出現(xiàn)阻塞Java進(jìn)程運(yùn)行的情況。

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

關(guān)鍵點(diǎn): Java Runtime.exec() 方法

基本方法: Runtime.exec()

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

即 在java中使用shell命令

這個(gè)方法有兩種使用形式:

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

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

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

執(zhí)行結(jié)果

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

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

查看錯(cuò)誤信息

	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);

簡(jiǎn)單示例

	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);

關(guān)鍵點(diǎn): python 絕對(duì)路徑

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

如:$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 的實(shí)現(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 ;
    //默認(rèn)的 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 命令時(shí),報(bào)找不到 可執(zhí)行程序(`Python` 命令)\
     *   ————建議: java 程序中使用的 `python` 命令的可執(zhí)行程序路徑,使用【絕對(duì)路徑】
     * @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è)務(wù)方法: 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è)務(wù)腳本的文件路徑, 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 腳本的標(biāo)準(zhǔn)輸出
        // ---- 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) );
        }
    }
}

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

相關(guān)文章

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

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

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

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

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

    Spring IO Platform簡(jiǎn)單介紹

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

    詳解Spring配置文件中bean的相關(guān)屬性

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

    ElasticSearch學(xué)習(xí)之Es索引Api操作

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

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

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

    將Java項(xiàng)目提交到云服務(wù)器的流程步驟

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

    深入理解Java設(shè)計(jì)模式之觀察者模式

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

    基于XML配置Spring的自動(dòng)裝配過程解析

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

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

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

最新評(píng)論