Java – Run shell script on a remote server
This article shows how to use JSch library to run or execute a shell script in a remote server via SSH.
<dependency>
<groupId>com.jcraft</groupId>
<artifactId>jsch</artifactId>
<version>0.1.55</version>
</dependency>
1. Run Remote Shell Script
This Java example uses JSch to SSH login a remote server (using password), and runs a shell script hello.sh
.
1.1 Here is a simple shell script in a remote server, IP address is 1.1.1.1
.
#! /bin/sh
echo "hello $1\n";
Assigned the execute permission.
$ chmod +x hello.sh
1.2 In local, we can use the below code to run or execute the above shell script in a remote server.
package com.mkyong.io.howto;
import com.jcraft.jsch.*;
import java.io.IOException;
import java.io.InputStream;
public class RunRemoteScript {
private static final String REMOTE_HOST = "1.1.1.1";
private static final String USERNAME = "";
private static final String PASSWORD = "";
private static final int REMOTE_PORT = 22;
private static final int SESSION_TIMEOUT = 10000;
private static final int CHANNEL_TIMEOUT = 5000;
public static void main(String[] args) {
String remoteShellScript = "/root/hello.sh";
Session jschSession = null;
try {
JSch jsch = new JSch();
jsch.setKnownHosts("/home/mkyong/.ssh/known_hosts");
jschSession = jsch.getSession(USERNAME, REMOTE_HOST, REMOTE_PORT);
// not recommend, uses jsch.setKnownHosts
//jschSession.setConfig("StrictHostKeyChecking", "no");
// authenticate using password
jschSession.setPassword(PASSWORD);
// 10 seconds timeout session
jschSession.connect(SESSION_TIMEOUT);
ChannelExec channelExec = (ChannelExec) jschSession.openChannel("exec");
// run a shell script
channelExec.setCommand("sh " + remoteShellScript + " mkyong");
// display errors to System.err
channelExec.setErrStream(System.err);
InputStream in = channelExec.getInputStream();
// 5 seconds timeout channel
channelExec.connect(CHANNEL_TIMEOUT);
// read the result from remote server
byte[] tmp = new byte[1024];
while (true) {
while (in.available() > 0) {
int i = in.read(tmp, 0, 1024);
if (i < 0) break;
System.out.print(new String(tmp, 0, i));
}
if (channelExec.isClosed()) {
if (in.available() > 0) continue;
System.out.println("exit-status: "
+ channelExec.getExitStatus());
break;
}
try {
Thread.sleep(1000);
} catch (Exception ee) {
}
}
channelExec.disconnect();
} catch (JSchException | IOException e) {
e.printStackTrace();
} finally {
if (jschSession != null) {
jschSession.disconnect();
}
}
}
}
Output
hello mkyong
exit-status: 0
Note
For JSch UnknownHostKey
exception, please add the remote server ip into the .ssh/known_hosts
, read this.
2. Run Remote Command
2.1 The below example is very similar to the above example#1. Instead, it uses a private key id_rsa
to SSH login a remote server, makes sure the remote server configured the public key correctly.
jsch.addIdentity("/home/mkyong/.ssh/id_rsa");
2.2 And we changed the command to ls
, the rest of the codes are the same.
channelExec.setCommand("ls -lsah");
2.3 This Java example runs a remote server command ls -lsah
to display the directory listing.
package com.mkyong.io.howto;
import com.jcraft.jsch.*;
import java.io.IOException;
import java.io.InputStream;
public class RunRemoteCommand {
private static final String REMOTE_HOST = "1.1.1.1";
private static final String USERNAME = "";
private static final int REMOTE_PORT = 22;
private static final int SESSION_TIMEOUT = 10000;
private static final int CHANNEL_TIMEOUT = 5000;
public static void main(String[] args) {
Session jschSession = null;
try {
JSch jsch = new JSch();
jsch.setKnownHosts("/home/mkyong/.ssh/known_hosts");
jschSession = jsch.getSession(USERNAME, REMOTE_HOST, REMOTE_PORT);
// not recommend, uses jsch.setKnownHosts
//jschSession.setConfig("StrictHostKeyChecking", "no");
// authenticate using private key
jsch.addIdentity("/home/mkyong/.ssh/id_rsa");
// 10 seconds timeout session
jschSession.connect(SESSION_TIMEOUT);
ChannelExec channelExec = (ChannelExec) jschSession.openChannel("exec");
// Run a command
channelExec.setCommand("ls -lsah");
// display errors to System.err
channelExec.setErrStream(System.err);
InputStream in = channelExec.getInputStream();
// 5 seconds timeout channel
channelExec.connect(CHANNEL_TIMEOUT);
// read the result from remote server
byte[] tmp = new byte[1024];
while (true) {
while (in.available() > 0) {
int i = in.read(tmp, 0, 1024);
if (i < 0) break;
System.out.print(new String(tmp, 0, i));
}
if (channelExec.isClosed()) {
if (in.available() > 0) continue;
System.out.println("exit-status: "
+ channelExec.getExitStatus());
break;
}
try {
Thread.sleep(1000);
} catch (Exception ee) {
}
}
channelExec.disconnect();
} catch (JSchException | IOException e) {
e.printStackTrace();
} finally {
if (jschSession != null) {
jschSession.disconnect();
}
}
}
}
Output
otal 48K
4.0K drwx------ 6 root root 4.0K Aug 4 07:57 .
4.0K drwxr-xr-x 22 root root 4.0K Sep 11 2019 ..
8.0K -rw------- 1 root root 5.5K Aug 3 08:50 .bash_history
4.0K -rw-r--r-- 1 root root 570 Jan 31 2010 .bashrc
4.0K drwx------ 2 root root 4.0K Dec 15 2019 .cache
4.0K drwx------ 2 root root 4.0K Dec 15 2019 .docker
4.0K -rwxr-xr-x 1 root root 31 Aug 4 07:54 hello.sh
4.0K -rw-r--r-- 1 root root 148 Aug 17 2015 .profile
4.0K drwx------ 2 root root 4.0K Aug 3 05:32 .ssh
4.0K drwxr-xr-x 2 root root 4.0K Aug 3 04:42 test
4.0K -rw------- 1 root root 1.2K Aug 4 07:57 .viminfo
exit-status: 0
2.4 Now, we test an invalid command abc
// Run a invalid command
channelExec.setCommand("abc");
Output
bash: abc: command not found
exit-status: 127
The errors will output to the System.err
.
Download Source Code
$ git clone https://github.com/mkyong/core-java
$ cd java-io
Thanks mkyong!! your, some portion of solution works for me & I am able to run the shell scripts which is placed on AWS EMR Cluster. After spending almost 6-8 hours.