gpt4 book ai didi

java - 通过 ProcessBuilder 运行时 Shell 脚本挂起

转载 作者:行者123 更新时间:2023-12-01 16:30:17 25 4
gpt4 key购买 nike

我有一个 Java 程序,我正在其中触发 shell 脚本。Java 代码示例是:

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

其中 cmdList 包含执行 shell 所需的所有必要输入参数。该 shell 脚本内部有一个 for 循环,并在该循环中执行一些数据库脚本,并在文件中打印结果信息和错误日志。

下面是示例 shell 脚本代码:

#!/bin/bash

导出 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=日期 '+%F %T'SCRIPT_START_TIME_IN_SEC=日期 +%s

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

陷阱'err=$?; logError 2>&1“清除期间发生错误。在 $LINENO: ${BASH_COMMAND} 行处退出,状态为 $err。请检查日志以获取更多信息。” >>$PROCESS_LOG'错误trap 'logError 2>&1“清除期间发生错误。由于收到外部中断而退出 shell 脚本执行。请检查日志以获取更多信息。” >>$PROCESS_LOG;陷阱 ERR' INT

横幅(){ echo “+------------------------------------------------------------ --------------------------------------------------+ ” printf "|tput 粗体[ %-40s tput sgr0|\n""$1 ] tput setaf 2 $2" echo “+------------------------------------------------------------ --------------------------------------------------+ ”}

日志错误(){ printf "[ProcessId- $$] [日期 "+%Y-%m-%d %H:%M:%S"] tput setaf 1 tput 粗体 [错误] tput setaf 1 %-40s tput sgr0\n""$@"}

日志信息(){ printf "[ProcessId- $$] [date "+%Y-%m-%d %H:%M:%S"] tput setaf 6 粗体 [INFO ] %-40s tput sgr0\n""$@"}日志警告(){ printf "[ProcessId- $$] [date "+%Y-%m-%d %H:%M:%S"] tput setaf 3 tput 粗体 [警告] %-40s tput sgr0\n""$@"}

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

主函数(){
横幅“$SCRIPT_START_TIME_FORMATTED”“已开始处理”|三通-a $PROCESS_LOG logInfo "在 $SCRIPT_START_TIME_FORMATTED 开始执行"|三通-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 脚本时,我观察到以下行为。

    a.它在 for 循环中进行一定的迭代后挂起。

    b.随着我减少 shell 脚本中的日志条目数量,迭代(for 循环)数量不断增加。

    c.当我删除所有信息日志并继续仅打印错误日志时,它成功完成。

有人可以帮助确定此行为背后的原因吗?

目前,我检查了 for 循环中的迭代次数,但是当我开始接收多个错误日志时,该问题可能随时发生。

问候

库沙格拉

最佳答案

您必须使用流程流或映射 errout文件,以便 native 缓冲区不会填满。如果您创建线程来使用每个流,效果会更好。 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();
}
}

关于java - 通过 ProcessBuilder 运行时 Shell 脚本挂起,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62056644/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com