gpt4 book ai didi

java - 如何在 Java 中启动 shell 终端 session 并使用它来放置命令并读取其结果以保持先前的上下文

转载 作者:太空宇宙 更新时间:2023-11-04 10:12:27 25 4
gpt4 key购买 nike

我有一个必须镜像 Linux shell 的 Web 应用程序,也就是说,用户将有一个模拟 shell 的屏幕,并在服务器端有一个实际的 shell。

我已经实现了一个快速但肮脏的解决方案来测试与 shell Process 的交互,但它似乎不起作用:这是一个简单的 Spring Controller,用户可以在其中传递命令并获得结果:

@RestController
public class ShController{

@GetMapping("/sh")
public DeferredResult<String> shSessionCommand(@RequestParam String command) throws IOException, InterruptedException {
DeferredResult<String> dr = new DeferredResult<>();

ProcessBuilder pb = new ProcessBuilder("sh");
//START SHELL
Process p = pb.start();

InputStream inputStream = p.getInputStream();
Thread t = new Thread(() -> {
//BACKGROUND SHELL OUTPUT READ
byte[] read = new byte[2048];
try {
inputStream.read(read);
dr.setResult(new String(read));
} catch (IOException e) {
e.printStackTrace();
}
});
t.start();
//TO ALLOW THE SHELL READER TO START BEFORE PASSING THE COMMAND
Thread.sleep(5000);
//WRITE THE USER COMMAND
OutputStream outputStream = p.getOutputStream();
outputStream.write(command.getBytes());
outputStream.flush();

return dr;
}
}

当我调用 /sh?command=ls outputStream.write(command.getBytes()); 写入 ls inputStream.read(read) 永远被阻塞。为什么?

更新:

说明:

  1. 每个用户都需要有自己的shell进程
  2. 每个用户 shell session 都将使用很长时间来保持其上下文

我已经更新了 Controller 以反射(reflect)这一点。有一个 map ,我在其中保存用户与其 shell 进程之间的关系。现在,我关闭了 OutputStream,inputStream.read(read); 确实读取了命令结果,但是,只是第一次。后续发出更多命令的调用在调用 outputStream.write(command.getBytes()); 时抛出 java.io.IOException: Stream closed。有什么想法吗?

@RestController
public class ShController{

private Map<String, Process> shellMap = new HashMap<>();

@GetMapping("/sh")
public DeferredResult<String> shSessionCommand(@RequestParam String command, HttpSession session) throws IOException, InterruptedException {
DeferredResult<String> dr = new DeferredResult<>();

Process p = getUserShell(session.getId());

InputStream inputStream = p.getInputStream();
Thread t = new Thread(() -> {
System.out.println("****THREAD");
byte[] read = new byte[2048];
try {
inputStream.read(read);
dr.setResult(new String(read));
} catch (IOException e) {
e.printStackTrace();
}
});
t.start();

Thread.sleep(1000);
System.out.println("****COMMAND");
OutputStream outputStream = p.getOutputStream();
outputStream.write(command.getBytes());
outputStream.flush();
outputStream.close();

return dr;
}

private Process getUserShell(String user) throws IOException {
if(this.shellMap.get(user) == null){
System.out.println("****Creating process");
ProcessBuilder pb = new ProcessBuilder("sh");
Process process = pb.start();
shellMap.put(user, process);
}
return shellMap.get(user);
}
}

最佳答案

问题是我没有发送新行(输入)字符,就好像我从未提交过命令一样。这是解决方案:

@RestController
public class ShController{

private Map<String, Process> shellMap = new HashMap<>();

@GetMapping("/sh")
public String shSessionCommand(@RequestParam String command, HttpSession session) throws IOException, InterruptedException {

String result = "";
Process process = getUserShell(session.getId());
InputStream out = process.getInputStream();
OutputStream in = process.getOutputStream();

byte[] buffer = new byte[4000];
boolean read = false;
boolean written = false;
while (!read) {
int no = out.available();
if (no > 0) {
int n = out.read(buffer, 0, Math.min(no, buffer.length));
result = new String(buffer, 0, n);
read = true;
}

if(!written) {
in.write((command + "\n").getBytes());
in.flush();
written = true;
}

try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}

return result;
}

private Process getUserShell(String user) throws IOException {
if(this.shellMap.get(user) == null){
System.out.println("****Creating process");
ProcessBuilder pb = new ProcessBuilder("sh", "-i");
Process process = pb.start();
shellMap.put(user, process);
}
return shellMap.get(user);
}
}

关于java - 如何在 Java 中启动 shell 终端 session 并使用它来放置命令并读取其结果以保持先前的上下文,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48183317/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com