bash命令在tomcat/java Runtime.getRuntime.exec()中失败,但可以从命令行运行
我有一个TomcatWebApp,它在shell中运行一个进程,因为java中没有几个实用程序此代码在其他机器上运行良好,但在我的公共服务器上有一个神秘的问题bash命令在tomcat/java Runtime.getRuntime.exec()中失败,但可以从命令行运行,java,linux,bash,shell,tomcat,Java,Linux,Bash,Shell,Tomcat,我有一个TomcatWebApp,它在shell中运行一个进程,因为java中没有几个实用程序此代码在其他机器上运行良好,但在我的公共服务器上有一个神秘的问题 String[] textAnalysisPipeline = { "/bin/sh", "-c", "/bin/cat " + inputfileLoc + " | tee /tmp/debug1 | " + loadJar + " " + jarOptLookupLoc + " " + optHfstL
String[] textAnalysisPipeline = {
"/bin/sh",
"-c",
"/bin/cat " + inputfileLoc +
" | tee /tmp/debug1 | " + loadJar + " " + jarOptLookupLoc + " " + optHfstLoc +
" 2>/dev/null | " + "tail -n+5" + // get rid of the header that hfst-ol.jar produces
" | tee /tmp/debug2 | cut -f 1-2" + // get rid of the "0.0" weights
" | tee /tmp/debug3 | " + cgConvLoc +
" | tee /tmp/debug4 | " + vislcg3Loc + " -g " + vislcg3DisGrammarLoc + // disambiguate with the constraint grammar
" | tee /tmp/debug5 > " + outputfileLoc};
log.debug("Text analysis pipeline: "+textAnalysisPipeline[2]);
Process process = Runtime.getRuntime().exec(textAnalysisPipeline);
process.waitFor();
我将字符串打印到日志中,它看起来是这样的:(path/to/
不是实际的路径)
如果我从日志中复制这个管道,并从bash命令行运行它,我将得到所需的输出,一直到管道的末尾。但是,当tomcat服务器运行该命令时,它会生成一个空文件。调试文件debug1
和debug2
与预期一样,但debug3
及其后为空,这表明管道在cut-f 1-2
时失败(请参阅下面的更新1)
OS-Fedora 22java-openjdk 1.8.077
tomcat-7.0.39
sh-->bash-4.3.42(1)-发布 ===============================================================================
更新1: 这似乎不是
cut
的问题。我编写了一个简短的python脚本,cut.py
,以实现与cut-f1-2
相同的功能(从每行末尾删除'\t0.0'
)
使用cut.py
代替cut
,我得到了同样的问题。对于服务器debug3
和更高版本,它是空的,但是如果我将粘贴从日志复制到交互式shell,一切都会正常工作
===============================================================================更新2:
我还编写了一个简单的bash脚本来运行管道,以便tomcat/java只运行带有一个输入/输出文件名参数的bash脚本。如果我从一个交互式shell运行脚本,它会工作,但是在tomcat中结果没有什么不同,在shell脚本中使用
cut
或cut.py
。您可以使用ProcessBuilder
。使用单字符串形式的Runtime.exec
不是一种好的样式。更好的选择是使用ProcessBuilder
,自己分割参数,而不是依靠Java来分割参数,这是非常幼稚的
ProcessBuilder pb = new ProcessBuilder("/bin/bash", "-c", /*...*/);
pb.redirectErrorStream(true);
Process p = pb.start();
这主要是一种旁白,或者是一种变通方法,但您的管道可以大大简化。可以用单个Awk脚本替换
cat
和tail
和cut
tee /tmp/debug1 <path/to/inputFile |
java -jar path/to/hfst-ol.jar path/to/analyser.ohfst 2>/dev/null |
tee /tmp/debug2 |
awk -F '\t' 'NR>5 { print $1 FS $2 }' |
tee /tmp/debug3 |
/usr/local/bin/cg-conv |
tee /tmp/debug4 |
path/to/vislcg3 -g path/to/grammar.rlx |
tee /tmp/debug5 > path/to/outputFile
tee/tmp/debug1/dev/null|
三通/tmp/debug2|
awk-F'\t''NR>5{print$1 FS$2}'|
三通/tmp/debug3|
/usr/local/bin/cg conv|
三通/tmp/debug4|
path/to/vislcg3-g path/to/grammar.rlx|
tee/tmp/debug5>path/to/outputFile
脚本和(有时尤其是)crontab中有一种模式,用于脚本的$PATH
变量与用于交互式shell的变量不同。这导致命令在一个环境中神秘地失败,但在其他环境中工作
我强烈建议将您正在使用的所有工具的完整路径嵌入到您正在构建的调用中。运行该进程时,
cut
可能位于未拾取的目录中。输入完整路径,如/usr/bin/cut
,将确保两件事:没有人会在您的路径中潜入意外版本的cut
,而且,如果您所在的环境中没有/usr/bin
在$PATH
中,这也无关紧要。当您从日志中复制命令并从命令行运行它时,您是否以与运行Tomcat的用户相同的用户身份执行它
Tomcat用户可能没有足够的权限读取命令中的一个文件
在我的Ubuntu系统上,默认的tomcat7用户称为tomcat7。可以使用以下命令以其他用户的身份运行命令:
sudo -u tomcat7 /bin/sh -c 'myCommand'
来源:。默认情况下,在大多数系统上,Tomcat与您的用户没有相同的环境。例如,它不会运行.bashrc.profile或登录脚本中的任何内容,因此用户shell环境中设置的所有变量都是不同的 您可以通过将
env
命令与两个用户(您的用户)进行比较,并让java程序调用它来检查这一点,例如:
String[] textAnalysisPipeline = {"/bin/sh","-c","/usr/bin/env > /tmp/env.txt"}; //or wherever the 'env' command is in your system
Process process = Runtime.getRuntime().exec(textAnalysisPipeline);
...
并将/tmp/env.txt
的内容与用户执行的env
进行比较。。。他们可能非常不同
查找以下变量:
- 路径
- 类路径
- 爪哇之家酒店
PATH
变量的脚本(不要忘记jar文件中的JAVA\u HOME
和CLASSPATH
).检查Startup.sh和Catalina.sh,寻找合适的位置放置您的物品/tmp/exec.log
),而不是消息中的/dev/null
)因此,您可以确定shell执行问题。我打赌它将类似于:sh:***:command not found
或错误:无法访问jarfile
或应用程序无法定位对象的某些消息,因此您可以确定脚本中调用的应用程序不在路径中变量或其中没有的LIB…通常两者都有
希望这能有所帮助……我建议实际响应命令行
sudo -u tomcat7 /bin/sh -c 'myCommand'
String[] textAnalysisPipeline = {"/bin/sh","-c","/usr/bin/env > /tmp/env.txt"}; //or wherever the 'env' command is in your system
Process process = Runtime.getRuntime().exec(textAnalysisPipeline);
...