bash 使用 shell 脚本在 -XX:OnOutOfMemoryError 上重新启动过程给出:无法识别的选项:-9
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/26684777/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Restarting process on -XX:OnOutOfMemoryError using shell script gives: Unrecognized option: -9
提问by DaTval
I'm trying to restart process when OOME happens. Java binary is launched using two shell scripts, one of them imports other. I don't have any control of the first one but can modify the second one as I want. This is a prototype what I'm trying to do:
我正在尝试在 OOME 发生时重新启动进程。Java 二进制文件使用两个 shell 脚本启动,其中一个导入另一个。我对第一个没有任何控制权,但可以根据需要修改第二个。这是我正在尝试做的原型:
First shell script test.sh:
第一个shell脚本test.sh:
#!/bin/sh
JAVA_OPTS="$JAVA_OPTS -Xmx10m"
. test1.sh
echo $JAVA_OPTS
java $JAVA_OPTS $es_params TestMemory
Second shell script test1.sh:
第二个 shell 脚本 test1.sh:
#!/bin/sh
pidfile="test.pid"
touch $pidfile
params="$parms -Dpidfile=$pidfile"
kill_command="kill -9 $(cat $pidfile)"
dir=$( cd $(dirname Unrecognized option: -9
Error: Could not create the Java Virtual Machine.
Error: A fatal exception has occurred. Program will exit.
) ; pwd -P )
path="$dir/$(basename Error: Could not find or load main class "-XX:OnOutOfMemoryError=kill
)"
start_command="$path $@"
restart_command="$kill_command;sleep 2;$start_command"
JAVA_OPTS="$JAVA_OPTS -XX:OnOutOfMemoryError=\"$restart_command\""
Generally what it does is JAVA_OPTS is constructed inside test1.sh and then used to run Java binary, which just writes PID in pidfile and then creates OOME.
一般它的作用是在test1.sh里面构造JAVA_OPTS,然后用来运行Java二进制文件,它只是在pidfile中写入PID然后创建OOME。
Problem happens during execution, java can't understand what is a parameter and what is a class to run. I think it might be a problem of quoting, I tried different ways to escape JAVA_OPTS, but without any result. I'm either getting:
执行过程中出现问题,java无法理解什么是参数,什么是要运行的类。我认为可能是引用的问题,我尝试了不同的方法来逃避JAVA_OPTS,但没有任何结果。我要么得到:
+ java -Xmx10m '"-XX:OnOutOfMemoryError=kill' '$(cat' 'test.pid);sleep' '2;/Users/davidt/test/TestMemory/bin/test.sh' '")' -Des.pidfile=test.pid TestMemory
Or
或者
java "-XX:OnOutOfMemoryError=echo 'Ups'" $es_params TestMemory
If I just take a value of JAVA_OPTS and put it manually in test.sh it runs perfectly. Any ideas how can I change test1.sh to make it work? I think I tried almost every possible way of putting double and single quotes, but without any success. Also if I put restart_command in restart.sh file and use it instead of the variable, it works fine.
如果我只是取一个 JAVA_OPTS 的值并将其手动放入 test.sh 中,它就会完美运行。任何想法如何更改 test1.sh 以使其工作?我想我尝试了几乎所有可能的放置双引号和单引号的方法,但没有任何成功。此外,如果我将 restart_command 放在 restart.sh 文件中并使用它而不是变量,它工作正常。
After running set -x I saw that shell modifies every single space character to ' ' - adds ' on both sides. Escaping doesn't gives any result. Any idea how to avoid this? So final commend is:
运行 set -x 后,我看到 shell 将每个空格字符修改为 ' ' - 在两侧添加 '。逃避不会产生任何结果。知道如何避免这种情况吗?所以最后的赞扬是:
JAVA_OPTS="\"-XX:OnOutOfMemoryError=echo 'Ups'\""
set -x
java $JAVA_OPTS TestMemory
Update
更新
I can run simplified command successfully
我可以成功运行简化命令
java '"-XX:OnOutOfMemoryError=echo' ''\''Ups'\''"' TestMemory
But it seems a general problem, shell just hates spaces into variables I guess:
但这似乎是一个普遍的问题,我猜shell只是讨厌将空格转换为变量:
java -Xmx10m -XX:OnOutOfMemoryError="kill NNNN;sleep 2;/Users/davidt/test/TestMemory/bin/test.sh" -Des.pidfile=test.pid TestMemory
This script fails and the last line is interpreted as:
此脚本失败,最后一行被解释为:
java -Xmx10m -XX:OnOutOfMemoryError="kill $(cat test.pid); ..."
I tried different options to escape
我尝试了不同的选择来逃避
回答by Stephen C
This is a shell problem. Based on the evidence, I'd say that one of the ;
characters ... and possibly some why space ... is being interpretted by the shell when you don't want / need this to happen.
这是壳的问题。根据证据,我会说其中一个;
字符……可能还有一些为什么空格……当您不希望/不需要发生这种情况时,shell 会对其进行解释。
If you run set -x
in the shell before running the command that is trying to start the JVM, you will see the actual command that is being used.
如果set -x
在运行试图启动 JVM 的命令之前在 shell 中运行,您将看到正在使用的实际命令。
It seems shell translates every single space to ' ',
似乎 shell 将每个空格都转换为 ' ',
Not exactly. The single quotes are inserted by the shell into the output you are getting from set -x
. They simply indicating where the argument boundaries are. They are not really there ... and they are certainly NOT being passed to the java
command.
不完全是。单引号由 shell 插入到您从中获得的输出中set -x
。它们只是表明参数边界在哪里。它们并不真的存在……而且它们肯定不会传递给java
命令。
Any idea how to [a]void it?
知道如何[a]避免它吗?
What you need to do is start from the (final) command that you are trying execute ...
您需要做的是从您尝试执行的(最终)命令开始...
#!/bin/bash
kill -9 $PPID
... and work backwards, so that the shell variables, expansions and escaping give you what you need.
...然后向后工作,以便 shell 变量、扩展和转义为您提供所需的内容。
The other thing to note is that this:
另一件需要注意的是:
##代码##probably won't work. The kill $(cat test.pid)
command is using shell syntax and requires shell functionality to interpolate the contents of the PID file. I doubt that the JVM is going to know what to do with that. (Or more accurately. It will do what you have literally told it to do, but that will not be what you want ...)
可能行不通。该kill $(cat test.pid)
命令使用 shell 语法并需要 shell 功能来插入 PID 文件的内容。我怀疑 JVM 是否会知道如何处理它。(或者更准确地说。它会做你字面上告诉它要做的事情,但这不是你想要的......)
If you really need to interpolate the pid file content when the restart command is runas you appear to be trying to do, then suggest that turn the restart command into a free-standing shell script, and set the file mode so that it is executable. It will be simpler and a lot easier to get working.
如果您确实需要在重新启动命令运行时插入 pid 文件内容,那么建议将重新启动命令转换为独立的 shell 脚本,并将文件模式设置为可执行. 工作会更简单,更容易。
As a general piece of advice, is is a bad idea to be too clever with shell scripts. The exact semantics of variable expansion and command parsing are rather tricky, and it is easy to get yourself really confused ... if you are trying to do this at multiple levels.
作为一般建议,使用 shell 脚本过于聪明是一个坏主意。变量扩展和命令解析的确切语义相当棘手,而且很容易让自己感到困惑……如果您尝试在多个级别执行此操作。
回答by DaTval
I ended up put the script I wanted to execute in a separate file and gave it as a parameter to JVM to execute when OOME happens.
echo "echo 'UPS'" >> oome_happened.sh
JAVA_OPTS="\"-XX:OnOutOfMemoryError='oome_happened.sh'\""
set -x
java $JAVA_OPTS TestMemory
我最终将我想要执行的脚本放在一个单独的文件中,并将它作为参数提供给 JVM,以便在 OOME 发生时执行。
echo "echo 'UPS'" >> oome_happened.sh
JAVA_OPTS="\"-XX:OnOutOfMemoryError='oome_happened.sh'\""
set -x
java $JAVA_OPTS TestMemory
回答by zczhuohuo
Like @DaTval said, you should put the command in a script. The script should be someting like.
就像@DaTval 所说的那样,您应该将命令放入脚本中。脚本应该是这样的。
##代码##Kill the caller of scripts.
杀死脚本的调用者。