我试图使用JSch库通过SSH协议执行多个命令。但我似乎陷入困境,无法找到任何解决方案。 setCommand()
方法每个会话只能执行一个命令。但我想按顺序执行命令,就像Android平台上的connectbot应用程序一样。到目前为止,我的代码是:
package com.example.ssh;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Properties;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import com.jcraft.jsch.Channel;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;
public class ExampleSSH extends Activity {
/** Called when the activity is first created. */
EditText command;
TextView result;
Session session;
ByteArrayOutputStream baos;
ByteArrayInputStream bais;
Channel channel;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
bais = new ByteArrayInputStream(new byte[1000]);
command = (EditText) findViewById(R.id.editText1);
result = (TextView) findViewById(R.id.terminal);
}
public void onSSH(View v){
String username = "xxxyyyzzz";
String password = "aaabbbccc";
String host = "192.168.1.1"; // sample ip address
if(command.getText().toString() != ""){
JSch jsch = new JSch();
try {
session = jsch.getSession(username, host, 22);
session.setPassword(password);
Properties properties = new Properties();
properties.put("StrictHostKeyChecking", "no");
session.setConfig(properties);
session.connect(30000);
channel = session.openChannel("shell");
channel.setInputStream(bais);
channel.setOutputStream(baos);
channel.connect();
} catch (JSchException e) {
// TODO Auto-generated catch block
Toast.makeText(this, e.getMessage(), Toast.LENGTH_LONG).show();
}
}
else{
Toast.makeText(this, "Command cannot be empty !", Toast.LENGTH_LONG).show();
}
}
public void onCommand(View v){
try {
bais.read(command.getText().toString().getBytes());
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
baos = new ByteArrayOutputStream();
channel.setOutputStream(baos);
result.setText(baos.toString());
}
}
代码似乎连接到服务器,但我认为输入和输出数组缓冲区存在一些问题,因为根本没有输出。有人可以指导我如何正确处理服务器的输入和输出以获得所需的输出?
答案 0 :(得分:12)
该命令是一个String,可以是远程shell接受的任何内容。尝试
cmd1 ; cmd2 ; cmd3
按顺序运行多个命令。或
cmd1 && cmd2 && cmd3
运行命令直到失败。
即使这可能也有效:
cmd1
cmd2
cmd3
或在Java中:
channel.setCommand("cmd1\ncmd2\ncmd3");
旁注:不要将密码和用户名放入代码中。将它们放入属性文件并使用系统属性指定属性文件的名称。这样,您可以将文件保留在项目之外,并确保密码/用户名不会泄漏。
答案 1 :(得分:11)
如果您不必区分各个命令的输入或输出,Aaron的答案(连续提供所有命令,由\n
或;
分隔)都可以。< / p>
如果你必须单独处理它们,或者在之前的命令完成之前不知道后面的命令:你可以在同一个Session(即连接)上一个接一个地打开多个exec
-Channels(即在之前的一个关闭之后)。每个人都有自己的命令。 (但它们不共享环境,因此第一个命令中的cd
命令对后来的命令没有影响。)
您只需要注意拥有Session对象,而不是为每个命令创建一个新对象。
另一个选项是shell channel,然后将各个命令作为输入传递给远程shell(即通过流)。但是你必须注意不要将输入混合到一个命令和下一个命令(即只有当你知道命令正在做什么时,或者如果你有一个交互式用户可以为命令提供输入和下一个命令,并知道何时使用哪一个。)
答案 2 :(得分:3)
设置SCPInfo对象以保存用户名,密码,端口:22和ip。
List<String> commands = new ArrayList<String>();
commands.add("touch test1.txt");
commands.add("touch test2.txt");
commands.add("touch test3.txt");
runCommands(scpInfo, commands);
public static void runCommands(SCPInfo scpInfo, List<String> commands){
try {
JSch jsch = new JSch();
Session session = jsch.getSession(scpInfo.getUsername(), scpInfo.getIP(), scpInfo.getPort());
session.setPassword(scpInfo.getPassword());
setUpHostKey(session);
session.connect();
Channel channel=session.openChannel("shell");//only shell
channel.setOutputStream(System.out);
PrintStream shellStream = new PrintStream(channel.getOutputStream()); // printStream for convenience
channel.connect();
for(String command: commands) {
shellStream.println(command);
shellStream.flush();
}
Thread.sleep(5000);
channel.disconnect();
session.disconnect();
} catch (Exception e) {
System.err.println("ERROR: Connecting via shell to "+scpInfo.getIP());
e.printStackTrace();
}
}
private static void setUpHostKey(Session session) {
// Note: There are two options to connect
// 1: Set StrictHostKeyChecking to no
// Create a Properties Object
// Set StrictHostKeyChecking to no
// session.setConfig(config);
// 2: Use the KnownHosts File
// Manually ssh into the appropriate machines via unix
// Go into the .ssh\known_hosts file and grab the entries for the hosts
// Add the entries to a known_hosts file
// jsch.setKnownHosts(khfile);
java.util.Properties config = new java.util.Properties();
config.put("StrictHostKeyChecking", "no");
session.setConfig(config);
}
答案 3 :(得分:0)
尽量避免使用“壳”通道。 “外壳”通道旨在实现交互式会话,而不是自动执行命令。使用“壳”通道,您将面临许多不良副作用。
要自动执行命令,请使用“ exec”通道。
通常,您可以根据需要打开任意数量的“ exec”通道,并使用每个通道执行您的命令之一。您可以按顺序甚至并行打开通道。
有关“ exec”通道使用的完整示例,请参见JSch Exec.java
example。
这样,每个命令都在隔离的环境中执行。这可能是一个优点,但在某些情况下也可能是不可取的。
如果需要以以前的命令影响以后的命令的方式执行命令(例如更改工作目录或设置环境变量),则必须在同一通道中执行所有命令。为此,请使用服务器外壳的适当结构。在大多数系统上,您可以使用分号:
execChannel.setCommand("command1 ; command2 ; command3");
在* nix服务器上,您也可以使用&&
来使以下命令仅在前面的命令成功执行时执行:
execChannel.setCommand("command1 && command2 && command3");
另请参阅Execute a list of commands from an ArrayList using JSch exec in Java。
最复杂的情况是,当命令彼此依赖时,您需要先处理先前命令的结果,然后再继续执行其他命令。
当您有这种需要时,通常表示设计不好。如果这确实是解决您问题的唯一方法,请认真考虑。或者考虑实现服务器端外壳脚本以实现逻辑,而不是从Java代码远程进行。 Shell脚本具有更强大的技术来检查先前命令的结果,然后在JSch中使用SSH界面。
无论如何,请参见JSch Shell channel execute commands one by one testing result before proceeding。
旁注:请勿使用StrictHostKeyChecking=no
。参见JSch SFTP security with session.setConfig("StrictHostKeyChecking", "no");。