shelljava的简单介绍

发布时间:2022-11-28

本文目录一览:

  1. 在Java中执行某个shell命令,会怎么样?
  2. java怎么执行shell脚本
  3. 如何在java中执行shell脚本
  4. 怎么通过java去调用并执行shell脚本以及问题总结
  5. 用shell脚本怎样编译java工程

在Java中执行某个shell命令,会怎么样?

Java中执行某个shell命令会执行相应的命令。 java实现执行shell的算法如下:

public void execCommand(String command) throws IOException {
    Runtime runtime = Runtime.getRuntime();
    Process proc = runtime.exec(command);
    try {
        if (proc.waitFor() != 0) {
            System.err.println("exit value = " + proc.exitValue());
        }
        BufferedReader in = new BufferedReader(new InputStreamReader(proc.getInputStream()));
        StringBuffer stringBuffer = new StringBuffer();
        String line = null;
        while ((line = in.readLine()) != null) {
            stringBuffer.append(line+"-");
        }
        //这里返回执行结果,并打印。
        System.out.println(stringBuffer.toString());
    } catch (InterruptedException e) {
        System.err.println(e);
    }
}

比如:echo.sh 内容如下:echo "test java call shell and return value". 那么:

try {
    execCommand(../../shell/echo.sh);
} catch (IOException e) {
    e.printStackTrace();
}

就会打印出:test java call shell and return value

java怎么执行shell脚本

如果shell脚本和java程序运行在不同的服务器上,可以使用远程执行Linux命令执行包,使用ssh2协议连接远程服务器,并发送执行命令就行了,ganymed.ssh2相关mave配置如下,你可以自己百度搜索相关资料。 如果shell脚本和java程序在同一台服务器上, 这里不得不提到java的process类了。 process这个类是一个抽象类,封装了一个进程(你在调用linux的命令或者shell脚本就是为了执行一个在linux下执行的程序,所以应该使用process类)。 process类提供了执行从进程输入,执行输出到进程,等待进程完成,检查进程的推出状态,以及shut down掉进程。

<dependency>
    <groupId>com.ganymed.ssh2</groupId>
    <artifactId>ganymed-ssh2-build</artifactId>
    <version>210</version>
</dependency>

本地执行命令代码如下:

String shpath="/test/test.sh";    //程序路径
Process process = null;
String command1 = "chmod 777 " + shpath;
process = Runtime.getRuntime().exec(command1);
process.waitFor();

如何在java中执行shell脚本

通过CommandHelper.execute方法可以执行命令,该类实现

package javaapplication3;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
/**
 *
 * @author chenshu
 */
public class CommandHelper {
    //default time out, in millseconds
    public static int DEFAULT_TIMEOUT;
    public static final int DEFAULT_INTERVAL = 1000;
    public static long START;
    public static CommandResult exec(String command) throws IOException, InterruptedException {
        Process process = Runtime.getRuntime().exec(command);
        CommandResult commandResult = wait(process);
        if (process != null) {
            process.destroy();
        }
        return commandResult;
    }
    private static boolean isOverTime() {
        return System.currentTimeMillis() - START >= DEFAULT_TIMEOUT;
    }
    private static CommandResult wait(Process process) throws InterruptedException, IOException {
        BufferedReader errorStreamReader = null;
        BufferedReader inputStreamReader = null;
        try {
            errorStreamReader = new BufferedReader(new InputStreamReader(process.getErrorStream()));
            inputStreamReader = new BufferedReader(new InputStreamReader(process.getInputStream()));
            //timeout control
            START = System.currentTimeMillis();
            boolean isFinished = false;
            for (;;) {
                if (isOverTime()) {
                    CommandResult result = new CommandResult();
                    result.setExitValue(CommandResult.EXIT_VALUE_TIMEOUT);
                    result.setOutput("Command process timeout");
                    return result;
                }
                if (isFinished) {
                    CommandResult result = new CommandResult();
                    result.setExitValue(process.waitFor());
                    //parse error info
                    if (errorStreamReader.ready()) {
                        StringBuilder buffer = new StringBuilder();
                        String line;
                        while ((line = errorStreamReader.readLine()) != null) {
                            buffer.append(line);
                        }
                        result.setError(buffer.toString());
                    }
                    //parse info
                    if (inputStreamReader.ready()) {
                        StringBuilder buffer = new StringBuilder();
                        String line;
                        while ((line = inputStreamReader.readLine()) != null) {
                            buffer.append(line);
                        }
                        result.setOutput(buffer.toString());
                    }
                    return result;
                }
                try {
                    isFinished = true;
                    process.exitValue();
                } catch (IllegalThreadStateException e) {
                    // process hasn't finished yet
                    isFinished = false;
                    Thread.sleep(DEFAULT_INTERVAL);
                }
            }
        } finally {
            if (errorStreamReader != null) {
                try {
                    errorStreamReader.close();
                } catch (IOException e) {
                }
            }
            if (inputStreamReader != null) {
                try {
                    inputStreamReader.close();
                } catch (IOException e) {
                }
            }
        }
    }
}

CommandHelper类使用了CommandResult对象输出结果错误信息。该类实现

package javaapplication3;
/**
 *
 * @author chenshu
 */
public class CommandResult {
    public static final int EXIT_VALUE_TIMEOUT=-1;
    private String output;
    void setOutput(String error) {
        output=error;
    }
    String getOutput(){
        return output;
    }
    int exitValue;
    void setExitValue(int value) {
        exitValue=value;
    }
    int getExitValue(){
        return exitValue;
    }
    private String error;
    /**
     * @return the error
     */
    public String getError() {
        return error;
    }
    /**
     * @param error the error to set
     */
    public void setError(String error) {
        this.error = error;
    }
}

怎么通过java去调用并执行shell脚本以及问题总结

对于第一个问题:java抓取,并且把结果打包。那么比较直接的做法就是,java接收各种消息(db,metaq等等),然后借助于jstorm集群进行调度和抓取。 最后把抓取的结果保存到一个文件中,并且通过调用shell打包, 回传。 也许有同学会问,为什么不直接把java调用odps直接保存文件,答案是,我们的集群不是hz集群,直接上传odps速度很有问题,因此先打包比较合适。(这里不纠结设计了,我们回到正题)

java调用shell的方法

通过ProcessBuilder进行调度

这种方法比较直观,而且参数的设置也比较方便, 比如我在实践中的代码(我隐藏了部分业务代码):

ProcessBuilder pb = new ProcessBuilder("./" + RUNNING_SHELL_FILE, param1, param2, param3);
pb.directory(new File(SHELL_FILE_DIR));
int runningStatus = 0;
String s = null;
try {
    Process p = pb.start();
    try {
        runningStatus = p.waitFor();
    } catch (InterruptedException e) {
    }
} catch (IOException e) {
}
if (runningStatus != 0) {
}
return;

这里有必要解释一下几个参数:

  • RUNNING_SHELL_FILE:要运行的脚本
  • SHELL_FILE_DIR:要运行的脚本所在的目录; 当然你也可以把要运行的脚本写成全路径。
  • runningStatus:运行状态,0标识正常。 详细可以看java文档。
  • param1, param2, param3:可以在RUNNING_SHELL_FILE脚本中直接通过$1, $2, $3分别拿到的参数。

直接通过系统Runtime执行shell

这个方法比较暴力,也比较常用, 代码如下:

p = Runtime.getRuntime().exec(SHELL_FILE_DIR + RUNNING_SHELL_FILE + " "+param1+" "+param2+" "+param3);
p.waitFor();

我们发现,通过Runtime的方式并没有builder那么方便,特别是参数方面,必须自己加空格分开,因为exec会把整个字符串作为shell运行。

可能存在的问题以及解决方法

没权限运行

这个情况我们团队的朱东方就遇到了,在做DTS迁移的过程中,要执行包里面的shell脚本, 解压出来了之后,发现执行不了。 那么就按照上面的方法授权吧

java进行一直等待shell返回

这个问题估计更加经常遇到。 原因是, shell脚本中有echo或者print输出, 导致缓冲区被用完了! 为了避免这种情况, 一定要把缓冲区读一下, 好处就是,可以对shell的具体运行状态进行log出来。 比如上面我的例子中我会变成:

ProcessBuilder pb = new ProcessBuilder("./" + RUNNING_SHELL_FILE, keyword.trim(), taskId.toString(), fileName);
pb.directory(new File(CASPERJS_FILE_DIR));
int runningStatus = 0;
String s = null;
try {
    Process p = pb.start();
    BufferedReader stdInput = new BufferedReader(new InputStreamReader(p.getInputStream()));
    BufferedReader stdError = new BufferedReader(new InputStreamReader(p.getErrorStream()));
    while ((s = stdInput.readLine()) != null) {
        LOG.error(s);
    }
    while ((s = stdError.readLine()) != null) {
        LOG.error(s);
    }
    try {
        runningStatus = p.waitFor();
    } catch (InterruptedException e) {
    }

记得在start()之后, waitFor()之前把缓冲区读出来打log, 就可以看到你的shell为什么会没有按照预期运行。 这个还有一个好处是,可以读shell里面输出的结果, 方便java代码进一步操作。 也许你还会遇到这个问题,明明手工可以运行的命令,java调用的shell中某一些命令居然不能执行,报错:命令不存在! 比如我在使用casperjs的时候,手工去执行shell明明是可以执行的,但是java调用的时候,发现总是出错。 通过读取缓冲区就能发现错误日志了。 我发现即便自己把安装的casperjs的bin已经加入了path中(/etc/profile, 各种bashrc中)还不够。比如:

export NODE_HOME="/home/admin/node"
export CASPERJS_HOME="/home/admin/casperjs"
export PHANTOMJS_HOME="/home/admin/phantomjs"
export PATH=$PATH:$JAVA_HOME/bin:/root/bin:$NODE_HOME/bin:$CASPERJS_HOME/bin:$PHANTOMJS_HOME/bin

原来是因为java在调用shell的时候,默认用的是系统的/bin/下的指令。特别是你用root权限运行的时候。 这时候,你要在/bin下加软链了。针对我上面的例子,就要在/bin下加软链:

ln -s /home/admin/casperjs/bin/casperjs casperjs;
ln -s /home/admin/node/bin/node node;
ln -s /home/admin/phantomjs/bin/phantomjs phantomjs;

这样,问题就可以解决了。

如果是通过java调用shell进行打包,那么要注意路径的问题了

因为shell里面tar的压缩和解压可不能直接写:

tar -zcf /home/admin/data/result.tar.gz /home/admin/data/result

直接给你报错,因为tar的压缩源必须到路径下面, 因此可以写成

tar -zcf /home/admin/data/result.tar.gz -C /home/admin/data/ result

如果我的shell是在jar包中怎么办?

答案是:解压出来。再按照上面指示进行操作。 (1)找到路径

String jarPath = findClassJarPath(ClassLoaderUtil.class);
JarFile topLevelJarFile = null;
try {
    topLevelJarFile = new JarFile(jarPath);
    Enumeration<JarEntry> entries = topLevelJarFile.entries();
    while (entries.hasMoreElements()) {
        JarEntry entry = entries.nextElement();
        if (!entry.isDirectory() && entry.getName().endsWith(".sh")) {
            对你的shell文件进行处理
        }
    }

对文件处理的方法就简单了,直接touch一个临时文件,然后把数据流写入,代码:

FileUtils.touch(tempjline);
tempjline.deleteOnExit();
FileOutputStream fos = new FileOutputStream(tempjline);
IOUtils.copy(ClassLoaderUtil.class.getResourceAsStream(r), fos);
fos.close();

用shell脚本怎样编译java工程

编译java工程一般直接用IDE或者用Ant、Maven之类的工具,很少有人用纯shell来编译java工程。正好遇到这样一个需求,用这篇博文做一下记录。 案例:用eclipse写了一个java project,然后编译打成jar包。 这个可以采用eclipse自带的Export就可以导出jar了。但是为了软件自动化等巴拉巴拉的原因,采用存shell脚本编译。 如图所示,java project的名称为iec104,下面src是源文件目录,bin是所引用的jar包目录,现在要进行编译,并且打成jar,如果对jar有所了解,都知道jar有个MANIFEST.MF文件,iec104工程的MF文件内容如下: 就是制定了运行的main-class(有public static void main(String args[])的类)。 将这个文件拷贝到iec104的根目录下,然后执行脚本compile.sh,内容如下:

#!/bin/bash
cur_dir=$(pwd)
echo $cur_dir
function do_compile_iec104(){
    #    echo $cur_dir
    iec104=$cur_dir/iec104
    iec104_src=$cur_dir/iec104/src
    iec104_bin=$cur_dir/iec104/bin
    #    echo $iec104_src
    #    echo $iec104_bin
    iec104_class=$cur_dir/iec104/class
    # 将iec104的src目录下的所有java文件的名称存入到iec104/src/sources.list文件中
    rm -rf $iec104_src/sources.list
    find $iec104_src -name "*.java" > $iec104_src/sources.list
    cat  $iec104_src/sources.list
    # $iec104_class是存放编译的class文件的目录
    rm -rf $iec104_class
    mkdir $iec104_class
    # 这里开始编译java文件,注意这里的-encoding utf-8,刚开始并没有加入这个,然后就报了一堆错误,纠结了很久才发现,这里给各位提个醒了。
    javac -d $iec104_class -encoding utf-8 -classpath $iec104_bin/classes12.jar:$iec104_bin/junit-4.10.jar:$iec104_bin/log4j-1.2.17.jar:$iec104_bin/mysql-connector-java-5.0.5-bin.jar:$iec104_bin/RXTXcomm.jar -g -sourcepath $iec104_src @$iec104_src/sources.list
    # 由于用到了log4j,所以要将log4j的配置文件一并放入,如果没有用到,可以忽略这句
    cp $iec104_src/log4j.properties $iec104_class
    # 如果原来在iec104目录下有jar报就删除掉,因为要生成新的
    rm $iec104/iec104.jar
    # 这里要cd到存放class的目录,否则如果采用绝对路径编译,编译出来的jar包里面就是绝对路径了,这样就会有问题
    # jar -cvfm $iec104/iec104.jar $iec104/MANIFEST.MF $iec104_class/*这样是错误的
    cd $iec104_class
    jar -cvfm $iec104/iec104.jar $iec104/MANIFEST.MF *
    # 赋予可执行权限
    sudo chmod a+x $iec104/iec104.jar
}
do_compile_iec104
exit 0

运行这个脚本(linux下)就可以编译并且在iec104的目录下就可以看到iec104.jar文件了。 这里补充下java命令的基本知识: javac 用于编译Java文件,格式为:

java [options] [sourcefiles] [@files]

其中:

  • options:命令行选项;
  • sourcefiles:一个或多个要编译的源文件;
  • @files:一个或多个对源文件进行列表的文件,有时候要编译的文件很多,一个个敲命令会显得很长,也不方便修改,可以把要编译的源文件列在文件中,在文件名前加@,这样就可以对多个文件进行编译,对编译一个工程很有用,方便,省事。 有几个比较重要的选项:
  • -d 用于指定编译成的class文件的存放位置,缺省情况下不指定class文件的存放目录,编译的class文件将和源文件在同一目录下;
  • -classpath 可以简写成-cp,用于搜索编译所需的class文件,指出编译所用到的class文件的位置,如jar、zip或者其他包含class文件的目录,指定该选项会覆盖CLASSPATH的设定;
  • -sourcepath用于搜索编译所需的源文件(即java文件),指定要搜索的源文件的位置,如jar、zip或其他包含java文件的目录; 需要注意windows下和linux下文件路径分隔符和文件列表(即-classpath和-sourcepath指定的文件)分隔符的区别:
  • windows下文件路径分隔符用 \ ,文件列表分隔符用分号 ;
  • linux下文件路径分隔符用 / ,文件列表分隔符用冒号 :