我正在使用 jcraft.jsch 通道在 ssh 上运行几个命令,我看到每当我使用一些复杂的命令(使用 regexp 或使用管道)时,命令的输出都有一个意想不到的"前缀",该前缀由命令片段组合而成,或者整个命令被分割成用多个空格分隔的小块,并在这里和那里与"<"字符组合......
示例 1:
#ls /opt/qb/data2/oscar/process-manager-helper/*/var/core-dump/{2089,2090,2091,2092,2093,2094,2095}.*
ger-helper/*/var/core-dump/{20
<2/oscar/process-manager-helper/*/var/core-dump/{208 9,2090,2091,2092,ls: cannot access /opt/qb/data2/oscar/process-manager-helper/*/var/core-dump/2090.*: No such file or directory
ls: cannot access /opt/qb/data2/oscar/process-manager-helper/*/var/core-dump/2092.*: No such file or directory
ls: cannot access /opt/qb/data2/oscar/process-manager-helper/*/var/core-dump/2094.*: No such file or directory
ls: cannot access /opt/data2/oscar/process-manager-helper/*/var/core-dump/2095.*: No such file or directory
/opt/qb/data2/oscar/process-manager-helper/0/var/core-dump/2089.20141126-195527.213-00000000-opt.dell.srvadmin.bin.idracadm7.idracadm7.core.gz
/opt/qb/data2/oscar/process-manager-helper/0/var/core-dump/2091.20141126-201557.530-00000000-opt.dell.srvadmin.bin.idracadm7.idracadm7.core.gz
/opt/qb/data2/oscar/process-manager-helper/0/var/core-dump/2093.20141126-202822.524-00000000-opt.dell.srvadmin.bin.idracadm7.idracadm7.core.gz
示例 2:
#du -s --block-size=1 /opt/qb/data2/oscar/process-manager-helper/0/var/core-dump/2089.20141126-195527.213-00000000-opt.dell.srvadmin.bin.idracadm7.idracadm7.core.gz | awk { print $1; }
rvadmin.bin.idracad
<-195527.213-00000000-opt.dell.srvadmin.bin.idracadm 7.idracadwk
<ell.srvadmin.bin.idracadm7.idracadm7.core.gz | awk { print $1; }
86016
下面是重现它的示例代码(至少在我的设置中):
public static void main(String[] args) throws Exception
{
JSch myConnection = new JSch();
myConnection.setKnownHosts("/dev/null");
Session mySession = myConnection.getSession("root", "my-host.my-lab.com", 2222);
mySession.setPassword("password123");
Properties config = new Properties();
config.put("StrictHostKeyChecking", "no");
mySession.setConfig(config);
mySession.connect(15000);
Channel myChannel = mySession.openChannel("shell");
((ChannelShell) myChannel).setPtyType("exec");
InputStream fromServer = myChannel.getInputStream();
OutputStream toServer = myChannel.getOutputStream();
myChannel.connect();
String commandString = "du -s --block-size=1 /opt/base/dir/eventLog | awk '{ print $1; }'";
toServer.write((commandString + "n").getBytes());
toServer.flush();
Thread.sleep(5000);
StringBuffer result = new StringBuffer();
while (true)
{
int avail = fromServer.available();
if (avail > 0)
{
while (avail > 0)
{
byte[] buf = new byte[avail];
int bytesRead = 0;
if ((bytesRead = fromServer.read(buf)) < 0)
{
throw new IOException("connection is probably closed (can't read " + avail + " from server) - return value is " + bytesRead);
}
for (int i = 0; i < bytesRead; i++)
{
if (buf[i] >= 127 || buf[i] < 9 || (buf[i] >= 14 && buf[i] <= 31) || buf[i] == 11 || buf[i] == 12 || buf[i] == 8)
{
continue;
}
result.append((char) buf[i]);
}
avail = fromServer.available();
}
if (result.toString().trim().endsWith("#"))
{
System.out.println(result);
break;
}
}
}
}
请告知
Channel myChannel = mySession.openChannel("shell");
((ChannelShell) myChannel).setPtyType("exec");
[...]
String commandString = "du ...";
toServer.write((commandString + "n").getBytes());
您正在打开一个通常用于交互式会话的 shell 通道。然后,将命令字符串写入通道的输入流,模拟键入命令的字符。这种运行命令的方式将起作用,但远程系统的行为就像这是一个交互式终端会话一样。特别是,远程 shell 可能会打印命令提示符,而远程 PTY 可能会回显程序正在"键入"的文本。我认为这就是发生在你身上的事情 - 你正在运行的命令的输出与命令提示符和字符回显混合在一起。
应考虑使用ChannelExec
而不是ChannelShell
来运行远程命令。使用ChannelExec,远程系统直接调用您的命令(而不是调用shell并让您将命令馈送到shell的输入中)。命令退出时通道关闭,不应为字符回显或命令提示符而烦恼。
Jsch有一个ChannelExec的例子。您的代码如下所示:
ChannelExec chan = mySession.openChannel("exec");
chan.setCommand("du ... | awk ...");
// Set input and output streams
// Request a PTY if you think you need one
chan.connect();
chan.connect()
将打开通道并在远程系统上启动命令。远程命令的标准输出可以从getInputStream()
返回的输入流中读取,命令的标准错误可以从getErrStream()
返回的流中读取。或者你可以用这些流做其他事情;请参阅示例。