java 遠(yuǎn)程執(zhí)行Shell命令-通過ganymed-ssh2連接

一彤敛、實現(xiàn)遠(yuǎn)程執(zhí)行

此程序的目的是執(zhí)行遠(yuǎn)程機(jī)器上的Shell腳本。

【環(huán)境參數(shù)】

遠(yuǎn)程機(jī)器IP:192.168.243.21

用戶名:user

密碼:password

命令:python /data/applogs/bd-job/jobhandler/gluesource/employee.py

【具體步驟】

1梆暮、導(dǎo)入需要依賴的jar包治力。

<dependency>
   <groupId>ch.ethz.ganymed</groupId>
   <artifactId>ganymed-ssh2</artifactId>
   <version>262</version>
</dependency>

<dependency>
   <groupId>commons-io</groupId>
   <artifactId>commons-io</artifactId>
   <version>2.5</version>
</dependency>

2、編寫RemoteShellExecutor工具類苔埋。

package com.vicente.vicenteboot.test;

import java.io.*;
import java.nio.charset.Charset;
import ch.ethz.ssh2.ChannelCondition;
import ch.ethz.ssh2.Connection;
import ch.ethz.ssh2.Session;
import ch.ethz.ssh2.StreamGobbler;
import org.apache.commons.io.IOUtils;

public class RemoteShellExecutor {

    private Connection conn;
    /** 遠(yuǎn)程機(jī)器IP */
    private String ip;
    /** 用戶名 */
    private String osUsername;
    /** 密碼 */
    private String password;
    private String charset = Charset.defaultCharset().toString();

    private static final int TIME_OUT = 1000 * 5 * 60;

    public RemoteShellExecutor(String ip, String usr, String pasword) {
        this.ip = ip;
        this.osUsername = usr;
        this.password = pasword;
    }


    /**
     * 登錄
     * @return
     * @throws IOException
     */
    private boolean login() throws IOException {
        conn = new Connection(ip);
        conn.connect();
        return conn.authenticateWithPassword(osUsername, password);
    }

    /**
     * 執(zhí)行腳本
     *
     * @param cmds
     * @return
     * @throws Exception
     */
    public int exec(String cmds) throws Exception {
        InputStream stdOut = null;
        InputStream stdErr = null;
        String outStr = "";
        String outErr = "";
        int ret = -1;
        try {
            if (login()) {
                // Open a new {@link Session} on this connection
                Session session = conn.openSession();
                // Execute a command on the remote machine.
                session.execCommand(cmds);
                stdOut = new StreamGobbler(session.getStdout());
                outStr = processStream(stdOut, charset);

                stdErr = new StreamGobbler(session.getStderr());
                outErr = processStream(stdErr, charset);

                session.waitForCondition(ChannelCondition.EXIT_STATUS, TIME_OUT);

                System.out.println("outStr=" + outStr);
                System.out.println("outErr=" + outErr);

                ret = session.getExitStatus();
            } else {
                throw new Exception("登錄遠(yuǎn)程機(jī)器失敗" + ip); // 自定義異常類 實現(xiàn)略
            }
        } finally {
            if (conn != null) {
                conn.close();
            }
            IOUtils.closeQuietly(stdOut);
            IOUtils.closeQuietly(stdErr);
        }
        return ret;
    }

    private String processStream(InputStream in, String charset) throws Exception {
        byte[] buf = new byte[1024];
        StringBuilder sb = new StringBuilder();
        while (in.read(buf) != -1) {
            sb.append(new String(buf, charset));
        }
        return sb.toString();
    }

    public static void main(String args[]) throws Exception {
        RemoteShellExecutor executor = new RemoteShellExecutor("192.168.243.21", "user", "password");
        // 執(zhí)行myTest.sh 參數(shù)為java Know dummy
        System.out.println(executor.exec2("python /data/bluemoon/kettle/runScript/ods/fact_org_employee.py "));
    }
}

3纤掸、運(yùn)行結(jié)果
備份數(shù)據(jù)成功脐供。

4、說明:
getExitStatus方法的返回值
注:一般情況下shell腳本正常執(zhí)行完畢借跪,getExitStatus方法返回0政己。
此方法通過遠(yuǎn)程命令取得Exit Code/status。但并不是每個server設(shè)計時都會返回這個值掏愁,如果沒有則會返回null歇由。

二、ganymed-ssh講解:

  1. Jar包:ganymed-ssh2-build210.jar
  2. 步驟:

a) 連接:

    Connection conn = new Connection(ipAddr);
    conn.connect();

b)認(rèn)證:

    boolean authenticateVal = conn.authenticateWithPassword(userName, password);

c) 打開一個Session:

    if(authenticateVal)
      Session session = conn.openSession();

d) 執(zhí)行Shell命令:

1)若是執(zhí)行簡單的Shell命令:(如 jps 果港、last 這樣的命令 )
        session.execCommand(cmd);

2) 遇到問題:
          用方法execCommand執(zhí)行Shell命令的時候沦泌,會遇到獲取不全環(huán)境變量的問題,
          比如執(zhí)行 hadoop fs -ls 可能會報找不到hadoop 命令的異常
          試著用execCommand執(zhí)行打印環(huán)境變量信息的時候辛掠,輸出的環(huán)境變量不完整
          與Linux主機(jī)建立連接的時候會默認(rèn)讀取環(huán)境變量等信息
          可能是因為session剛剛建立還沒有讀取完默認(rèn)信息的時候谢谦,execCommand就執(zhí)行了Shell命令
          
        解決:

        所以換了另外一種方式來執(zhí)行Shell命令:
          // 建立虛擬終端
          session.requestPTY("bash");
          // 打開一個Shell
          session.startShell();
          // 準(zhǔn)備輸入命令
          PrintWriter out = new PrintWriter(session.getStdin());   
                         // 輸入待執(zhí)行命令
          out.println(cmd);
          out.println("exit")
          // 6. 關(guān)閉輸入流
          out.close();
            // 7. 等待释牺,除非1.連接關(guān)閉;2.輸出數(shù)據(jù)傳送完畢回挽;3.進(jìn)程狀態(tài)為退出没咙;4.超時
          session.waitForCondition(ChannelCondition.CLOSED | ChannelCondition.EOF | ChannelCondition.EXIT_STATUS , 30000);       
        用這種方式執(zhí)行Shell命令,會避免環(huán)境變量讀取不全的問題千劈,第7步里有許多標(biāo)識可以用祭刚,比如當(dāng)exit命令執(zhí)行后或者超過了timeout時間,則session關(guān)閉

這里需要注意墙牌,當(dāng)一個Shell命令執(zhí)行時間過長時涡驮,會遇到ssh連接超時的問題,
        解決辦法:
          1. 之前通過把Linux主機(jī)的sshd_config的參數(shù)ClientAliveInterval設(shè)為60喜滨,同時將第7步中timeout時間設(shè)置很大捉捅,來保證命令執(zhí)行完畢,
            因為是執(zhí)行Mahout中一個聚類算法虽风,耗時最少7锯梁、8分鐘,數(shù)據(jù)量大的話焰情,需要幾個小時。

2. 后來將命令改成了nohup的方式執(zhí)行剥懒,nohup hadoop jar .... >> XXX.log && touch XXX.log.end &
            這種方式是提交到后臺執(zhí)行内舟,即使當(dāng)前連接斷開也會繼續(xù)執(zhí)行,把命令的輸出結(jié)果寫入日志初橘,如果hadoop命令執(zhí)行成功验游,則生成.end文件
            獲取文件的方法 ganymed-ssh2-build210.jar 也提供了,如下
              SCPClient scpClient = con.createSCPClient();
              scpClient.get("remoteFiles","localDirectory"); //從遠(yuǎn)程獲取文件

e) 獲取Shell命令執(zhí)行結(jié)果:
    InputStream stderr = new StreamGobbler(session.getStderr());
    InputStream in = new StreamGobbler(session.getStdout());

獲取流中的數(shù)據(jù):

private String processStdErr(InputStream in, String charset)
            throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(in, charset));
        StringBuffer sb = new StringBuffer();
        if (in.available() != 0) {
            while (true) {
                String line = br.readLine();
                if (line == null)
                    break;
     sb.append(line).append(System.getProperty("line.separator"));
            }
        }        
        return sb.toString();
    }

三保檐、使用實
1耕蝉、使用session.execCommand(cmds);來執(zhí)行命令會出現(xiàn)一個問題,就是還沒有加載完成服務(wù)器的環(huán)境變量就開始執(zhí)行命令了
這導(dǎo)致了很多情況是找不到命令夜只,解決的辦法就是:

/**
 * 執(zhí)行腳本
 *
 * @param cmds
 * @return
 * @throws Exception
 */
public int exec2(String cmds) throws Exception {
    InputStream stdOut = null;
    InputStream stdErr = null;
    String outStr = "";
    String outErr = "";
    int ret = -1;
    try {
        if (login()) {
            Session session = conn.openSession();
            // 建立虛擬終端
            session.requestPTY("bash");
            // 打開一個Shell
            session.startShell();
            stdOut = new StreamGobbler(session.getStdout());
            stdErr = new StreamGobbler(session.getStderr());
            BufferedReader stdoutReader = new BufferedReader(new InputStreamReader(stdOut));
            BufferedReader stderrReader = new BufferedReader(new InputStreamReader(stdErr));

            // 準(zhǔn)備輸入命令
            PrintWriter out = new PrintWriter(session.getStdin());
            // 輸入待執(zhí)行命令
            out.println(cmds);
            out.println("exit");
            // 6. 關(guān)閉輸入流
            out.close();
            // 7. 等待垒在,除非1.連接關(guān)閉;2.輸出數(shù)據(jù)傳送完畢扔亥;3.進(jìn)程狀態(tài)為退出场躯;4.超時
            session.waitForCondition(ChannelCondition.CLOSED | ChannelCondition.EOF | ChannelCondition.EXIT_STATUS , 30000);
            System.out.println("Here is the output from stdout:");
            while (true)
            {
                String line = stdoutReader.readLine();
                if (line == null)
                    break;
                System.out.println(line);
            }
            System.out.println("Here is the output from stderr:");
            while (true)
            {
                String line = stderrReader.readLine();
                if (line == null)
                    break;
                System.out.println(line);
            }
            /* Show exit status, if available (otherwise "null") */
            System.out.println("ExitCode: " + session.getExitStatus());
            ret = session.getExitStatus();
            session.close();/* Close this session */
            conn.close();/* Close the connection */

        } else {
            throw new Exception("登錄遠(yuǎn)程機(jī)器失敗" + ip); // 自定義異常類 實現(xiàn)略
        }
    } finally {
        if (conn != null) {
            conn.close();
        }
        IOUtils.closeQuietly(stdOut);
        IOUtils.closeQuietly(stdErr);
    }
    return ret;
}

2、scp復(fù)制一個文件到服務(wù)器上

/**
 * 遠(yuǎn)程傳輸單個文件
 *
 * @param localFile
 * @param remoteTargetDirectory
 * @throws IOException
 */

public void transferFile(String localFile, String remoteTargetDirectory) throws IOException {
    File file = new File(localFile);
    if (file.isDirectory()) {
        throw new RuntimeException(localFile + "  is not a file");
    }
    String fileName = file.getName();
    execCommand("mkdir -p " + remoteTargetDirectory);

    SCPClient sCPClient = connection.createSCPClient();
    SCPOutputStream scpOutputStream = sCPClient.put(fileName, file.length(), remoteTargetDirectory, "0600");

    String content = IOUtils.toString(new FileInputStream(file),StandardCharsets.UTF_8);
    scpOutputStream.write(content.getBytes());
    scpOutputStream.flush();
    scpOutputStream.close();
}

/**
 * 傳輸整個目錄
 *
 * @param localDirectory
 * @param remoteTargetDirectory
 * @throws IOException
 */
public void transferDirectory(String localDirectory, String remoteTargetDirectory) throws IOException {
    File dir = new File(localDirectory);
    if (!dir.isDirectory()) {
        throw new RuntimeException(localDirectory + " is not directory");
    }

    String[] files = dir.list();
    for (String file : files) {
        if (file.startsWith(".")) {
            continue;
        }
        String fullName = localDirectory + "/" + file;
        if (new File(fullName).isDirectory()) {
            String rdir = remoteTargetDirectory + "/" + file;
            execCommand("mkdir -p " + remoteTargetDirectory + "/" + file);
            transferDirectory(fullName, rdir);
        } else {
            transferFile(fullName, remoteTargetDirectory);
        }
    }

}

使用:

public static void main(String[] args) throws IOException {
    SSHAgent sshAgent = new SSHAgent();
    sshAgent.initSession("192.168.243.21", "user", "password#");
    sshAgent.transferFile("C:\\data\\applogs\\bd-job\\jobhandler\\2020-03-07\\483577870267060231.log","/data/applogs/bd-job/jobhandler/2018-09-13");
    sshAgent.close();
}

參考文章
Java遠(yuǎn)程執(zhí)行Shell命令
Ganymed SSH2 模擬類似FileZilla遠(yuǎn)程傳輸文件

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末旅挤,一起剝皮案震驚了整個濱河市踢关,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌粘茄,老刑警劉巖签舞,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件秕脓,死亡現(xiàn)場離奇詭異,居然都是意外死亡儒搭,警方通過查閱死者的電腦和手機(jī)吠架,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來师妙,“玉大人诵肛,你說我怎么就攤上這事∧ǎ” “怎么了怔檩?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長蓄诽。 經(jīng)常有香客問我薛训,道長,這世上最難降的妖魔是什么仑氛? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任乙埃,我火速辦了婚禮,結(jié)果婚禮上锯岖,老公的妹妹穿的比我還像新娘介袜。我一直安慰自己,他們只是感情好出吹,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布遇伞。 她就那樣靜靜地躺著,像睡著了一般捶牢。 火紅的嫁衣襯著肌膚如雪鸠珠。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天秋麸,我揣著相機(jī)與錄音渐排,去河邊找鬼。 笑死灸蟆,一個胖子當(dāng)著我的面吹牛驯耻,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播炒考,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼吓歇,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了票腰?” 一聲冷哼從身側(cè)響起城看,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎杏慰,沒想到半個月后测柠,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體炼鞠,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年轰胁,在試婚紗的時候發(fā)現(xiàn)自己被綠了谒主。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡赃阀,死狀恐怖霎肯,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情榛斯,我是刑警寧澤观游,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站驮俗,受9級特大地震影響懂缕,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜王凑,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一搪柑、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧索烹,春花似錦工碾、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至瓣戚,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間焦读,已是汗流浹背子库。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留矗晃,地道東北人仑嗅。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像张症,于是被迫代替她去往敵國和親仓技。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,916評論 2 344

推薦閱讀更多精彩內(nèi)容

  • 官網(wǎng) 中文版本 好的網(wǎng)站 Content-type: text/htmlBASH Section: User ...
    不排版閱讀 4,367評論 0 5
  • 小編費(fèi)力收集:給你想要的面試集合 1.C++或Java中的異常處理機(jī)制的簡單原理和應(yīng)用俗他。 當(dāng)JAVA程序違反了JA...
    八爺君閱讀 4,574評論 1 114
  • Ambari安裝部署Hadoop Apache Ambari是一種基于Web的工具脖捻,支持Apache Hadoop...
    三杯水Plus閱讀 2,632評論 0 7
  • 隨著全球經(jīng)濟(jì)的不斷發(fā)展,大數(shù)據(jù)時代早已悄悄到來兆衅,而Hadoop又是大數(shù)據(jù)環(huán)境的基礎(chǔ)地沮,想入門大數(shù)據(jù)行業(yè)首先需要了解H...
    如意粑粑閱讀 1,379評論 2 4
  • 我們沒有那么不好嗜浮,我們也沒有那么好。 友情摩疑,愛情危融,同時讓我明白了這個道理。 我竟然很平靜雷袋,情緒沒有我想象的波濤洶涌...
    MrR的兔小姐閱讀 168評論 0 1