簡體   English   中英

Shell 通過 ProcessBuilder 運行時腳本掛起

[英]Shell Scripts hangs when running through ProcessBuilder

我有一個 Java 程序,我在其中觸發 shell 腳本。 Java 代碼示例是:

 ProcessBuilder pb = new ProcessBuilder(cmdList);
        p = pb.start();
        p.waitFor();

其中 cmdList 包含執行 shell 所需的所有必要輸入參數。此 shell 腳本內部有一個 for 循環,並在該循環中執行一些數據庫腳本,並將結果信息和錯誤日志打印在一個文件中。

以下是示例 shell 腳本代碼:

#!/bin/bash

export PATH=/apps/PostgresPlus/as9.6/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

設置-eE

################################################## ## 開始

TIME_ELAPSED="" TIME_ELAPSED_IN_HOURS="" SCRIPT_START_TIME_FORMATTED= date '+%F %T' SCRIPT_START_TIME_IN_SEC= date +%s

PROCESS_LOG_BASE_PATH="/data/logs/purge_log/" PROCESS_LOG="$PROCESS_LOG_BASE_PATH/purge.log"

陷阱'err=$?; logError 2>&1 “清除期間發生錯誤。退出時狀態 $err 位於 $LINENO: ${BASH_COMMAND} 行。請檢查日志以獲取更多信息。” >>$PROCESS_LOG' ERR trap 'logError 2>&1 “清除期間發生錯誤。退出 shell 腳本執行,因為收到外部中斷。請檢查日志以獲取更多信息。” >>$進程日志; 陷阱 ERR' INT

banner() { echo "+---------------------------------------- ---------------------------------------------- ---+" printf "| tput bold [ %-40s tput sgr0 |\n" "$1 ] tput setaf 2 $2" echo "+-------------------- ---------------------------------------------- --------------------------+" }

logError() { printf "[ProcessId- $$] [ date "+%Y-%m-%d %H:%M:%S" ] tput setaf 1 tput bold [錯誤] tput setaf 1 %-40s tput sgr0 \n" "$@" }

logInfo(){ printf "[ProcessId- $$] [ date "+%Y-%m-%d %H:%M:%S" ] tput setaf 6 bold [INFO] %-40s tput sgr0 \n" " $@" } logWarn(){ printf "[ProcessId- $$] [ date "+%Y-%m-%d %H:%M:%S" ] tput setaf 3 tput bold [警告] %-40s tput sgr0 \n" "$@" }

logHint(){ printf "[ProcessId- $$] [ date "+%Y-%m-%d %H:%M:%S" ] tput setaf 5 tput sitm %-40s tput sgr0 \n" "$@ “}

主要的() {
橫幅“$SCRIPT_START_TIME_FORMATTED”“開始處理”| tee -a $PROCESS_LOG logInfo "在 $SCRIPT_START_TIME_FORMATTED 開始執行" | tee -a $PROCESS_LOG

 set PGPASSWORD=$DB_PASSWORD export PGPASSWORD=$DB_PASSWORD # Call DB function for audit and category wise data purging, population of schema names SCHEMA_NAMES_RESULT=$(psql -h $HOST_NAME -d $DB_NAME -U $DB_USER -p $DB_PORT -At -c "SELECT $COMMON_SCHEMA_NAME.purge_audit_and_populate_schema_names('$COMMON_SCHEMA_NAME', $PURGE_DATA_INTERVAL_IN_DAYS,'$SCHEMA_NAMES',$NUM_TOP_CONTRIBUTING_TENANTS)") SCHEMA_NAMES_RESULT=$(echo "$SCHEMA_NAMES_RESULT" | sed 's/{//g; s/}//g; s/"//g' ) SCHEMA_NAMES=$(echo $SCHEMA_NAMES_RESULT | rev | cut -d"," -f2- | rev) #Convert comma separated string of tenants to array SCHEMA_NAMES=($(echo "$SCHEMA_NAMES" | tr ',' '\n')) # loop for multi schema for element in "${SCHEMA_NAMES[@]}" do logInfo "Effective tenant - $element, Script start time - $SCRIPT_START_TIME_FORMATTED" | tee -a $PROCESS_LOG # PGSQL call to DB function to execute purging logInfo "Time elapsed since script execution started - $TIME_ELAPSED" | tee -a $PROCESS_LOG done #logInfo "Purge completed!" | tee -a $PROCESS_LOG logInfo "Purge execution completed successfully at `date '+%F %T'`" | tee -a $PROCESS_LOG exit 0

}

mkdir -p $PROCESS_LOG_BASE_PATH 主 "$@"

################################################## ## 結尾

以下是我對該程序的觀察。

  1. 當直接在 putty 上運行 shell 腳本時,它可以正確執行,沒有任何錯誤。
  2. 當通過上面的 java 程序觸發 shell 腳本時,我觀察到以下行為。

    一種。 它在 for 循環中的某個迭代后掛起。

    b. 當我減少 shell 腳本中的日志條目數量時,迭代(for 循環)數量不斷增加。

    c。當我刪除所有信息日志並繼續僅打印錯誤日志時,它成功完成。

有人可以幫助確定此行為背后的原因嗎?

現在,我檢查了 for 循環中的迭代次數,但是當我開始接收多個錯誤日志時,這個問題隨時可能發生。

問候

庫沙格拉

您必須使用流程流或 map err輸出到文件,這樣本機緩沖區就不會填滿。 如果你創建線程來消耗每個 stream,它會更好。hacky 單線程版本是這樣的:

ProcessBuilder pb = new ProcessBuilder(cmdList);
p = pb.start();
try (InputStream in = p.getInputStream();
            InputStream err = p.getErrorStream();
            OutputStream closeOnly = p.getOutputStream()) {
    while (p.isAlive()) {                
        long skipped = 0L;
        try {
            skipped = in.skip(in.available()) 
                     + err.skip(err.available());
        } catch (IOException jdk8155808) {
           byte[] b = new byte[2048];
           int read = in.read(b, 0, Math.min(b.length, in.available());
           if (read > 0) {
               skipped += read;
           }

           read = err.read(b, 0, Math.min(b.length, err.available());
           if (read > 0) {
               skipped += read;
           }
        }

        if(skipped == 0L) {
           p.waitFor(5L, TimeUnit.MILLISECONDS);
        }
    }
} finally {
   p.destroy();
}

螺紋方式是這樣工作的:

public void foo() {
    class DevNull implements Runnable {
        
        private final InputStream is;
        DevNull(final InputStream is) {
            is = Objects.requireNonNull(is);
        }
        
        public void run() {
            byte[] b = new byte[64];
            try {
                while (is.read(b) >= 0);
            } catch(IOException ignore) {
            }
        }
    }

    ExecutorService e = Executors.newCachedThreadPool();
    ProcessBuilder pb = new ProcessBuilder(cmdList);
    Process p = pb.start();
    try (InputStream in = p.getInputStream();
            InputStream err = p.getErrorStream();
            OutputStream closeOnly = p.getOutputStream()) {
        e.execute(new DevNull(in));
        e.execute(new DevNull(err));
        p.waitFor();
    } finally {
        p.destroy();
        e.shutdown();
    }
}

感謝多線程對我有用。

對於單線程選項,它在 skip() 上失敗。

再次感謝您幫助解決問題。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM