bash命令在tomcat / java Runtime.getRuntime.exec()中失败,但是从命令行

时间:2016-06-13 07:51:55

标签: java linux bash shell tomcat

我有一个tomcat webapp,它在shell中运行一个进程,因为java中没有几个实用程序。 此代码在其他计算机上运行良好,但我的公共服务器上存在一个神秘的问题。

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/不是实际路径)

/bin/cat /path/to/inputFile | tee /tmp/debug1 | java -jar /path/to/hfst-ol.jar /path/to/analyser.ohfst 2>/dev/null | tail -n+5 | tee /tmp/debug2 | cut -f 1-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

如果我从日志中复制此管道并从bash命令行运行它,我将获得所需的输出一直到管道的末尾。但是,当tomcat服务器运行该命令时,它会生成一个空文件。调试文件debug1debug2符合预期,但debug3及其后为空,这表示管道在cut -f 1-2处失败(请参阅下面的更新1)。

操作系统 - Fedora 22
java - openjdk 1.8.0_77
tomcat - 7.0.39
sh - > bash - 4.3.42(1)-release

=============================================== =================
更新1:

这似乎不是cut的问题。我编写了一个简短的python脚本cut.py来实现与cut -f 1-2相同的功能(从每行的末尾删除'\t0.0'

import re, sys
myRE = re.compile( r'\s+0\.0$' )
for line in sys.stdin :
    sys.stdout.write( myRE.sub( '', line ) )

使用cut.py代替cut,我遇到同样的问题。服务器debug3及更高版本是空的,但如果我从日志中复制粘贴到交互式shell,一切正常。

=============================================== =================
更新2:

我还编写了一个简单的bash脚本来运行管道,以便tomcat / java只运行带有输入/输出文件名的一个参数的bash脚本。如果我从交互式shell运行脚本,它可以工作,但是在shell脚本中使用cutcut.py在tomcat中的结果没有区别。

5 个答案:

答案 0 :(得分:3)

这主要是一个或一个解决方法,但您的管道可以显着简化。 catuselesstailcut可以替换为单个Awk脚本。

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

答案 1 :(得分:3)

默认情况下,在大多数系统上安装,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.txtenv执行的内容与您的用户进行比较......它们可能非常不同。

查找以下变量:

  • PATH
  • CLASSPATH
  • JAVA_HOME

我过去已经遇到过同样的问题。我建议采用以下方法:

  1. 使用所有内容的绝对路径,包括调用&#34; java&#34;,&#34; tee&#34;,&#34; tail&#34;和libs(jar文件)......你有你的命令;

  2. 更改运行Tomcat的环境配置,以访问您在命令中调用的所有应用程序(通常通过调用配置所有必需PATH变量的脚本(不要忘记{{1 }和JAVA_HOME到你的jar文件!)。检查Startup.sh和Catalina.sh是否包含你的东西;

  3. 更改命令以将错误输出重定向到消息中的CLASSPATH,而不是重定向到系统中某个应用程序可以写入的日志文件(通常/dev/null是很好)所以你可以确定shell执行问题。我打赌它会像:/tmp/exec.logsh: ****: command not found或者你的应用程序的某些消息无法找到一个对象,所以你将确定你在脚本中调用的应用程序不在Error: Unable to access jarfile环境变量或您不具备的库...通常都是

  4. 有关此问题的其他信息,请查看https://www.mulesoft.com/tcat/tomcat-classpath

    希望这会有所帮助......

答案 2 :(得分:1)

您可以使用ProcessBuilder。使用Runtime.exec的单字符串形式不是一个好的风格。更好的选择是使用ProcessBuilder,并自己拆分参数,而不是依靠Java来为你分割它们,它非常天真。

ProcessBuilder pb = new ProcessBuilder("/bin/bash", "-c", /*...*/);
pb.redirectErrorStream(true);
Process p = pb.start();

答案 3 :(得分:0)

脚本和(有时特别是)crontabs中存在一种模式,其中您用于脚本的$PATH变量与您用于交互式shell的变量不同。这导致命令在一个环境中神秘地失败,但在其他地方工作。

我强烈建议将您正在使用的所有工具的完整路径嵌入到您正在构建的调用中。在运行该进程时,cut可能位于未被拾取的目录中。像/usr/bin/cut这样的完整路径会产生两件事:没有人会将cut的意外版本偷偷带入你的道路,而且如果你的环境不是很重要在/usr/bin中没有$PATH

答案 4 :(得分:0)

从日志中复制命令并从命令行运行它时,是否以与运行Tomcat的用户相同的用户身份执行该命令?

Tomcat用户可能没有足够的权限来读取命令中的一个文件。

在我的Ubuntu系统上,默认的Tomcat 7用户名为tomcat7。可以使用sudo命令以另一个用户身份运行命令:

sudo -u tomcat7 /bin/sh -c 'myCommand'

来源:How do you process an entire command as sudo that involves redirecting or piping?