在-XX上重新启动进程:使用shell脚本的OnOutOfMemoryError给出:无法识别的选项:-9

时间:2014-10-31 23:19:59

标签: java linux bash shell escaping

我正在尝试在OOME发生时重启进程。使用两个shell脚本启动Java二进制文件,其中一个导入其他脚本。我对第一个没有任何控制权,但可以根据需要修改第二个。 这是我正在尝试做的原型:

第一个shell脚本test.sh:

#!/bin/sh
JAVA_OPTS="$JAVA_OPTS -Xmx10m"
. test1.sh

echo $JAVA_OPTS
java $JAVA_OPTS $es_params TestMemory

第二个shell脚本test1.sh:

#!/bin/sh

pidfile="test.pid"
touch $pidfile
params="$parms -Dpidfile=$pidfile"

kill_command="kill -9 \$(cat $pidfile)"

dir=$( cd $(dirname $0) ; pwd -P )
path="$dir/$(basename $0)"

start_command="$path $@"

restart_command="$kill_command;sleep 2;$start_command"

JAVA_OPTS="$JAVA_OPTS -XX:OnOutOfMemoryError=\"$restart_command\""

通常它的作用是JAVA_OPTS在test1.sh中构建,然后用于运行Java二进制文件,它只是在pidfile中写入PID然后创建OOME。

执行期间出现问题,java无法理解什么是参数以及什么是要运行的类。我认为这可能是引用的问题,我尝试了不同的方法来逃避JAVA_OPTS,但没有任何结果。我要么:

Unrecognized option: -9
Error: Could not create the Java Virtual Machine.
Error: A fatal exception has occurred. Program will exit.

或者

Error: Could not find or load main class "-XX:OnOutOfMemoryError=kill

如果我只取一个JAVA_OPTS的值并将其手动放在test.sh中,它就会完美运行。 任何想法如何更改test1.sh以使其工作?我想我几乎尝试了双重和单引号的所有可能方式,但没有任何成功。此外,如果我将restart_command放在restart.sh文件中并使用它而不是变量,它可以正常工作。

在运行set -x之后,我看到shell将每个空格字符修改为'' - 两侧添加'。转义不会产生任何结果。知道怎么避免这个吗?所以最后的赞扬是:

+ java -Xmx10m '"-XX:OnOutOfMemoryError=kill' '$(cat' 'test.pid);sleep' '2;/Users/davidt/test/TestMemory/bin/test.sh' '")' -Des.pidfile=test.pid TestMemory

更新

我可以成功运行简化命令

java "-XX:OnOutOfMemoryError=echo 'Ups'" $es_params TestMemory

但这似乎是一个普遍的问题,shell只是讨厌变量空间我想:

JAVA_OPTS="\"-XX:OnOutOfMemoryError=echo 'Ups'\""
set -x
java $JAVA_OPTS TestMemory

此脚本失败,最后一行解释为:

java '"-XX:OnOutOfMemoryError=echo' ''\''Ups'\''"' TestMemory 

我尝试了不同的选项来逃避

3 个答案:

答案 0 :(得分:2)

这是一个shell问题。根据证据,我说当你不想要/不需要这个时候,贝壳会解释其中一个;字符......以及可能是空间......发生。

如果在运行尝试启动JVM的命令之前在shell中运行set -x,您将看到正在使用的实际命令。


  

似乎shell将每个空间翻译为' ',

不完全是。 shell将单引号插入到set -x的输出中。它们只是指示参数边界的位置。它们并不存在......并且它们当然不会被传递给java命令。

  

知道如何[a]取消它吗?

您需要做的是从您尝试执行的(最终)命令开始......

java -Xmx10m -XX:OnOutOfMemoryError="kill NNNN;sleep 2;/Users/davidt/test/TestMemory/bin/test.sh" -Des.pidfile=test.pid TestMemory

...并向后工作,以便shell变量,扩展和转义为您提供所需。

另外需要注意的是:

java -Xmx10m -XX:OnOutOfMemoryError="kill $(cat test.pid); ..."

可能不会工作。 kill $(cat test.pid)命令使用shell语法,并且需要shell功能来插入PID文件的内容。我怀疑JVM将会知道如何处理它。 (或者更准确地说。它会按照字面意思告诉它去做,但那不是你想要的......)

如果您确实需要在运行“重启”命令时插入pid文件内容,那么建议您将restart命令转换为独立的shell脚本,并设置文件模式以使其可执行。工作起来会更简单,也更容易。

作为一般建议,使用shell脚本过于聪明是一个坏主意。变量扩展和命令解析的确切语义相当棘手,很容易让自己感到困惑......如果你想在多个层次上做这个。

答案 1 :(得分:1)

我最终将我想要执行的脚本放在一个单独的文件中,并将其作为参数提供给JVM,以便在OOME发生时执行。 echo "echo 'UPS'" >> oome_happened.sh JAVA_OPTS="\"-XX:OnOutOfMemoryError='oome_happened.sh'\"" set -x java $JAVA_OPTS TestMemory

答案 2 :(得分:0)

就像@DaTval说的那样,您应该将命令放在脚本中。脚本应该像这样。

#!/bin/bash

kill -9 $PPID

杀死脚本的调用者。