[英]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 主 "$@"
################################################## ## 結尾以下是我對該程序的觀察。
當通過上面的 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.