Why does JShell have an echo in my shell demo? - java

I am writing a simple shell demo that highlights a problem I am having with JShell. When I pass JShell as an argument to /bin/zsh -c it works fine except for the fact that my Process echoes back whatever I send it.
Here is the code to my shell demo.
import java.io.*;
public class ShellDemo {
private String[] cmd = new String[] {"/bin/zsh", "-c", "jshell"};
private Process process;
public ShellDemo() {}
public void init() {
try {
ProcessBuilder pb = new ProcessBuilder(cmd);
process = pb.start();
read();
listen();
} catch (IOException ex) {
System.err.println(ex);
}
}
private void read() {
Thread thread = new Thread(() -> {
try (BufferedReader reader = process.inputReader()) {
char[] buf = new char[10000];
while (reader.ready() || process.isAlive()) {
int count = reader.read(buf, 0, 10000);
System.out.print(new String(buf, 0, count));
}
int exitVal = process.waitFor();
System.out.println("Process exited with value " + exitVal + "\n");
} catch (IOException | InterruptedException ex) {
System.err.println(ex);
}
});
thread.start();
}
private void listen() {
Thread thread = new Thread(() -> {
try (
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter writer = process.outputWriter();
) {
while (process.isAlive()) {
String line = reader.readLine();
writer.write(line);
writer.newLine();
writer.flush();
}
} catch (IOException ex) {
System.err.println(ex);
}
});
thread.start();
}
public static void main(String[] args) {
ShellDemo demo = new ShellDemo();
demo.init();
}
}
Then I compile and run my shell demo, and you'll see the echo in the output.
% javac ShellDemo.java
% java ShellDemo
| Welcome to JShell -- Version 17.0.1
| For an introduction type: /help intro
jshell> System.out.println("hello world");
System.out.println("hello world")hello world
jshell>
See how System.out.println("hello world") gets echoed before the "hello world" message is read by the inputReader? Why does zsh or JShell echo what I send it?
Is there a way to avoid the echo? What's the best solution?

Related

ProcessBuilder in java not executing the 'java -jar' command

I am trying to execute the windows' commands(which we can execute through command prompt), through java code, using processbuilder, and get response. My program works fine for commands like 'dir', but doesnt work for 'java -jar'. When I give 'java -jar C:\mypath\filename.jar' it is waiting for ever and even not getting any input from the stream. Can you please help on this.
Note: It is working fine if we do that through command prompt.
public byte[] execute(String command) {
ProcessBuilder builder = new ProcessBuilder();
LOG.info("Executing the command {} ...", command);
builder.command("cmd.exe", "/c", command);
builder.redirectErrorStream(true);
try {
Process process = builder.start();
StringBuffer outStringBuffer = new StringBuffer();
StreamGobbler streamGobbler = new StreamGobbler(process.getInputStream(), x -> {
outStringBuffer.append(x).append("\n");
});
Future<?> future = Executors.newSingleThreadExecutor().submit(streamGobbler);
future.get(120, TimeUnit.SECONDS);//Waiting for 120 seconds and throwing error with null.
int exitCode = process.waitFor();// If we skip prev step, waiting for ever for this process.
System.out.println("ExitCode:"+exitCode);
// assert exitCode == 0;
if (exitCode == 0) {
LOG.info("Command \'{}\' executed. And response:{}", command, outStringBuffer.toString());
return outStringBuffer.toString().getBytes();
} else {
LOG.error("Command \'{}\' executed. And process exited with exitCode:{}. Response stored:{}.", command,
exitCode, outStringBuffer.toString());
return ("Process exited with exitCode" + exitCode).getBytes();
}
} catch (IOException | InterruptedException | ExecutionException | TimeoutException e) {
LOG.error("Error in execution of command:{}. Details of error:{}.{}", command, e.getMessage(), e);
return ("Error in execution of command:" + command + ". Details of error:" + e.getMessage() + "." + e)
.getBytes();
}
}
public class StreamGobbler implements Runnable {
private InputStream inputStream;
private Consumer<String> consumer;
public StreamGobbler(InputStream inputStream, Consumer<String> consumer) {
this.inputStream = inputStream;
this.consumer = consumer;
}
#Override
public void run() {
new BufferedReader(new InputStreamReader(inputStream)).lines().forEach(consumer);
}
}
Realized, it is mistake with handling the Future and process if the subprocess is taking too long to finish. Updated the code to handle.
try {
future.get(120, TimeUnit.SECONDS);
} catch (TimeoutException e) {
readerTimedOut = true;
LOG.info("The Reader for the process did not complete till timeout seconds:{}", 120);
}
int processTimeOutSeconds = 40;
if (readerTimedOut) {
processTimeOutSeconds = 0;
}
boolean isProcessTerminated = process.waitFor(processTimeOutSeconds, TimeUnit.SECONDS);

Java Interacting with Command Propmt [duplicate]

This question already has answers here:
Execute external program through terminal in Java
(4 answers)
Executing another java program from our java program [duplicate]
(3 answers)
Closed 4 years ago.
Using
String cmdString = "cmd.exe /c start python ";
Process p = Runtime.getRuntime().exec(cmdString);
I can open the command prompt and run python. I now want to interact with the command prompt. I have read that using
public static void main(String[] args)
{
BufferedWriter writerToProc;
String scriptPath = "C:\\Users\\MichaelMi\\Documents\\SourceTree\\NODE-Sensor-Configurator\\src\\application\\resources\\BACnet-CMD-Line-Upgrader\\UpgradeApplication.py";
String iniPath = "C:\\Users\\MichaelMi\\Documents\\SourceTree\\NODE-Sensor-Configurator\\src\\application\\resources\\BACnet-CMD-Line-Upgrader\\BACpypes.ini";
String execString = "python " + scriptPath + " --ini " + iniPath;
String cmdString = "cmd.exe /c start " + execString ;
try {
Process p = Runtime.getRuntime().exec(cmdString);
writerToProc = new BufferedWriter(new OutputStreamWriter(p.getOutputStream()));
writerToProc.write(cmdString);
writerToProc.flush();
writerToProc.write("whois\n");
writerToProc.flush();
readErrors(p);
readOutput(p);
} catch (IOException e) {
e.printStackTrace();
}
}
public static void readOutput(Process p)
{
BufferedReader stdInput = new BufferedReader(new InputStreamReader(p.getInputStream()));
Runnable task = new Runnable() {
#Override
public void run() {
try {
if(stdInput.ready())
{
stdInput.lines().forEach((l) -> System.out.println(l));
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
};
Thread backgroundThread = new Thread(task);
backgroundThread.setDaemon(true);
backgroundThread.start();
}
public static void readErrors(Process p)
{
BufferedReader stdError = new BufferedReader(new InputStreamReader(p.getErrorStream()));
Runnable task = new Runnable() {
#Override
public void run() {
try {
if(stdError.ready())
{
stdError.lines().forEach((l) -> System.out.println(l));
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
};
Thread backgroundThread = new Thread(task);
backgroundThread.setDaemon(true);
backgroundThread.start();
}
Is supposed to allow me to write to the open command prompt. However this is not working for me. I am getting no exceptions thrown or status errors. I simply do not know how to write to an open command prompt.
I see two problems in your code:
One problem is the used command-line:
cmd.exe /c start python This starts a new cmd.exe instance which itself the uses start to start a detached python process. The detached process is therefore not connected to your BufferedReader/BufferedWriter.
Your second problem is that python does not execute your "1+1" via stdin.
You can simply verify that by creating a file test with the context 1+1\n and execute it on the console: python < test. You will see no output.
See also piping from stdin to a python code in a bash script.
In this case you need to close the input stream before you can read the output streams of the python process. If anyone knows a better way please let us know.
public static void main(String[] args) {
String cmdString = "python";
try {
ProcessBuilder pb = new ProcessBuilder(cmdString);
Process pr = pb.start();
try (BufferedReader readerOfProc = new BufferedReader(
new InputStreamReader(pr.getInputStream()));
BufferedReader errorsOfProc = new BufferedReader(
new InputStreamReader(pr.getErrorStream()))) {
try (BufferedWriter writerToProc = new BufferedWriter(
new OutputStreamWriter(pr.getOutputStream()));) {
writerToProc.write("myVar=1+1\r\n");
writerToProc.write("print(myVar)\r\n");
writerToProc.flush();
} catch (Exception e) {
e.printStackTrace();
}
String s;
while ((s = readerOfProc.readLine()) != null) {
System.out.println("stdout: " + s);
}
while ((s = errorsOfProc.readLine()) != null) {
System.out.println("stdout: " + s);
}
System.out.println("exit code: " + pr.waitFor());
}
} catch (Exception e) {
e.printStackTrace();
}
}
Hope this helps!

Run a shell file in java

In Java you can call a shell file like this:
public class Shell {
private static Shell rootShell = null;
private final Process proc;
private final OutputStreamWriter writer;
private Shell(String cmd) throws IOException {
this.proc = new ProcessBuilder(cmd).redirectErrorStream(true).start();
this.writer = new OutputStreamWriter(this.proc.getOutputStream(), "UTF-8");
}
public void cmd(String command) {
try {
writer.write(command+'\n');
writer.flush();
} catch (IOException e) { }
}
public void close() {
try {
if (writer != null) {
writer.close();
if(proc != null) {
proc.destroy();
}
}
} catch (IOException ignore) {}
}
public static void exec(String command) {
Shell.get().cmd(command);
}
public static Shell get() {
if (Shell.rootShell == null) {
while (Shell.rootShell == null) {
try {
Shell.rootShell = new Shell("su"); //Open with Root Privileges
} catch (IOException e) { }
}
}
return Shell.rootShell;
}
}
Shell.exec("echo " + bt.getLevel() + " > "+ flashfile);
right.
but I have a shell which giving an argument after executing it.
how can I pass that argument? I don't want user type anything to run this shell file. in another word, I want to fully automate a shell file.
If you want to automate a shell file with a Java programme, this can be done. You could even pipe a series of commands to this programme saved in a file and executing these as a batch.
You can execute commands batches of commands from like this:
java -cp experiments-1.0-SNAPSHOT.jar ConsoleReader < commands.txt
commands.txt is a file with a series of commands:
cmd /k date
cmd /k dir
netstat
ipconfig
Or you can with the same programme allow the user to execute commands on the command line.
Below you can find a sample programme which you can compile and be run in the above described manner.
What does it do?
It hooks a java.util.Scanner to the console input and consumes each line.
Then it spawns two threads which listen to the error and input streams and write out either to stderr or stdin.
Empty lines on the console are ignored
If you type "read " it will execute the commands on that file.
Source:
public class ConsoleReader {
public static void main(String[] args) throws IOException, DatatypeConfigurationException {
try(Scanner scanner = new Scanner(new BufferedInputStream(System.in), "UTF-8")) {
readFromScanner(scanner);
}
}
private static final Pattern FILE_INPUT_PAT = Pattern.compile("read\\s*([^\\s]+)");
private static void readFromScanner(Scanner scanner) {
while (scanner.hasNextLine()) {
try {
String command = scanner.nextLine();
if(command != null && !command.trim().isEmpty()) {
command = command.trim();
if("exit".equals(command)) {
break; // exit shell
}
else if(command.startsWith("read")) { // read from file whilst in the shell.
readFile(command);
}
else {
Process p = Runtime.getRuntime().exec(command);
Thread stdout = readFromStream(p.getInputStream(), System.out, "in");
Thread stderr = readFromStream(p.getErrorStream(), System.err, "err");
stdout.join(200);
stderr.join(200);
}
}
}
catch(Exception e) {
Logger.getLogger("ConsoleReader").log(Level.SEVERE, String.format("Failed to execute command %s", e));
}
}
}
private static void readFile(String command) throws FileNotFoundException {
Matcher m = FILE_INPUT_PAT.matcher(command);
if(m.matches()) {
String file = m.group(1);
File f = new File(file);
if (f.exists()) {
try (Scanner subScanner = new Scanner(f)) {
readFromScanner(subScanner);
}
}
}
else {
System.err.printf("Oops, could not find '%s'%n", command);
}
}
private static Thread readFromStream(InputStream stdin, PrintStream out, String name) throws IOException {
Thread thread = new Thread(() -> {
try (BufferedReader in = new BufferedReader(new InputStreamReader(stdin))) {
String line;
while ((line = in.readLine()) != null) {
out.println(line);
}
} catch (IOException e) {
Logger.getLogger("ConsoleReader").log(Level.SEVERE, "Failed to read from stream.", e);
}
}, name);
thread.setDaemon(true);
thread.start();
return thread;
}
}
Runtime.getRuntime().exec("src/[FILE LOCATION]");
I think this is the command you're looking for. Let me know if it works!

DOSBox commands through Java Program-word gets removed

String run="c:\\Program Files\DOSBox-0.74\dosbox.exe dosbox -c mount c c:\games";
The word c c:\games gets removed.
Please advise how do I prevent this? Should I use a literal to insert the spaces in the command?
A little bit of experimentation...
public class Test {
public static void main(String[] args) {
try {
ProcessBuilder pb = new ProcessBuilder("C:\\Program Files (x86)\\DOSBox-0.74\\DOSBox.exe", "dosbox", "-c", "mount c c:\\games");
pb.redirectError();
Process p = pb.start();
new Thread(new InputStreamConsumer(p.getInputStream())).start();
System.out.println("Have exited with " + p.waitFor());
} catch (IOException | InterruptedException exp) {
exp.printStackTrace();
}
}
public static class InputStreamConsumer implements Runnable {
private InputStream is;
public InputStreamConsumer(InputStream inputStream) {
is = inputStream;
}
#Override
public void run() {
int in = -1;
try {
while ((in = is.read()) != -1) {
System.out.print((char) in);
}
} catch (IOException exp) {
exp.printStackTrace();
}
}
}
}

How to run Linux commands in Java?

I want to create diff of two files. I tried searching for code in Java that does it, but didnt find any simple code/ utility code for this. Hence, I thought if I can somehow run linux diff/sdiff command from my java code and make it return a file that stores the diff then it would be great.
Suppose there are two files fileA and fileB. I should be able to store their diff in a file called fileDiff through my java code. Then fetching data from fileDiff would be no big deal.
You can use java.lang.Runtime.exec to run simple code. This gives you back a Process and you can read its standard output directly without having to temporarily store the output on disk.
For example, here's a complete program that will showcase how to do it:
import java.io.BufferedReader;
import java.io.InputStreamReader;
public class testprog {
public static void main(String args[]) {
String s;
Process p;
try {
p = Runtime.getRuntime().exec("ls -aF");
BufferedReader br = new BufferedReader(
new InputStreamReader(p.getInputStream()));
while ((s = br.readLine()) != null)
System.out.println("line: " + s);
p.waitFor();
System.out.println ("exit: " + p.exitValue());
p.destroy();
} catch (Exception e) {}
}
}
When compiled and run, it outputs:
line: ./
line: ../
line: .classpath*
line: .project*
line: bin/
line: src/
exit: 0
as expected.
You can also get the error stream for the process standard error, and output stream for the process standard input, confusingly enough. In this context, the input and output are reversed since it's input from the process to this one (i.e., the standard output of the process).
If you want to merge the process standard output and error from Java (as opposed to using 2>&1 in the actual command), you should look into ProcessBuilder.
You can also write a shell script file and invoke that file from the java code. as shown below
{
Process proc = Runtime.getRuntime().exec("./your_script.sh");
proc.waitFor();
}
Write the linux commands in the script file, once the execution is over you can read the diff file in Java.
The advantage with this approach is you can change the commands with out changing java code.
You need not store the diff in a 3rd file and then read from in. Instead you make use of the Runtime.exec
Process p = Runtime.getRuntime().exec("diff fileA fileB");
BufferedReader stdInput = new BufferedReader(new InputStreamReader(p.getInputStream()));
while ((s = stdInput.readLine()) != null) {
System.out.println(s);
}
try to use unix4j. it s about a library in java to run linux command. for instance if you got a command like:
cat test.txt | grep "Tuesday" | sed "s/kilogram/kg/g" | sort
in this program will become:
Unix4j.cat("test.txt").grep("Tuesday").sed("s/kilogram/kg/g").sort();
You can call run-time commands from java for both Windows and Linux.
import java.io.*;
public class Test{
public static void main(String[] args)
{
try
{
Process process = Runtime.getRuntime().exec("pwd"); // for Linux
//Process process = Runtime.getRuntime().exec("cmd /c dir"); //for Windows
process.waitFor();
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line;
while ((line=reader.readLine())!=null)
{
System.out.println(line);
}
}
catch(Exception e)
{
System.out.println(e);
}
finally
{
process.destroy();
}
}
}
Hope it Helps.. :)
Runtime run = Runtime.getRuntime();
//The best possible I found is to construct a command which you want to execute
//as a string and use that in exec. If the batch file takes command line arguments
//the command can be constructed a array of strings and pass the array as input to
//the exec method. The command can also be passed externally as input to the method.
Process p = null;
String cmd = "ls";
try {
p = run.exec(cmd);
p.getErrorStream();
p.waitFor();
}
catch (IOException e) {
e.printStackTrace();
System.out.println("ERROR.RUNNING.CMD");
}finally{
p.destroy();
}
The suggested solutions could be optimized using commons.io, handling the error stream, and using Exceptions. I would suggest to wrap like this for use in Java 8 or later:
public static List<String> execute(final String command) throws ExecutionFailedException, InterruptedException, IOException {
try {
return execute(command, 0, null, false);
} catch (ExecutionTimeoutException e) { return null; } /* Impossible case! */
}
public static List<String> execute(final String command, final long timeout, final TimeUnit timeUnit) throws ExecutionFailedException, ExecutionTimeoutException, InterruptedException, IOException {
return execute(command, 0, null, true);
}
public static List<String> execute(final String command, final long timeout, final TimeUnit timeUnit, boolean destroyOnTimeout) throws ExecutionFailedException, ExecutionTimeoutException, InterruptedException, IOException {
Process process = new ProcessBuilder().command("bash", "-c", command).start();
if(timeUnit != null) {
if(process.waitFor(timeout, timeUnit)) {
if(process.exitValue() == 0) {
return IOUtils.readLines(process.getInputStream(), StandardCharsets.UTF_8);
} else {
throw new ExecutionFailedException("Execution failed: " + command, process.exitValue(), IOUtils.readLines(process.getInputStream(), StandardCharsets.UTF_8));
}
} else {
if(destroyOnTimeout) process.destroy();
throw new ExecutionTimeoutException("Execution timed out: " + command);
}
} else {
if(process.waitFor() == 0) {
return IOUtils.readLines(process.getInputStream(), StandardCharsets.UTF_8);
} else {
throw new ExecutionFailedException("Execution failed: " + command, process.exitValue(), IOUtils.readLines(process.getInputStream(), StandardCharsets.UTF_8));
}
}
}
public static class ExecutionFailedException extends Exception {
private static final long serialVersionUID = 1951044996696304510L;
private final int exitCode;
private final List<String> errorOutput;
public ExecutionFailedException(final String message, final int exitCode, final List<String> errorOutput) {
super(message);
this.exitCode = exitCode;
this.errorOutput = errorOutput;
}
public int getExitCode() {
return this.exitCode;
}
public List<String> getErrorOutput() {
return this.errorOutput;
}
}
public static class ExecutionTimeoutException extends Exception {
private static final long serialVersionUID = 4428595769718054862L;
public ExecutionTimeoutException(final String message) {
super(message);
}
}
if the opening in windows
try {
//chm file address
String chmFile = System.getProperty("user.dir") + "/chm/sample.chm";
Desktop.getDesktop().open(new File(chmFile));
} catch (IOException ex) {
Logger.getLogger(Frame.class.getName()).log(Level.SEVERE, null, ex);
{
JOptionPane.showMessageDialog(null, "Terjadi Kesalahan", "Error", JOptionPane.WARNING_MESSAGE);
}
}
ProcessBuilder processBuilder = new ProcessBuilder();
// -- Linux --
// Run a shell command
processBuilder.command("bash", "-c", "ls /home/kk/");
// Run a shell script
//processBuilder.command("path/to/hello.sh");
// -- Windows --
// Run a command
//processBuilder.command("cmd.exe", "/c", "dir C:\\Users\\kk");
// Run a bat file
//processBuilder.command("C:\\Users\\kk\\hello.bat");
try {
Process process = processBuilder.start();
StringBuilder output = new StringBuilder();
BufferedReader reader = new BufferedReader(
new InputStreamReader(process.getInputStream()));
String line;
while ((line = reader.readLine()) != null) {
output.append(line + "\n");
}
int exitVal = process.waitFor();
if (exitVal == 0) {
System.out.println("Success!");
System.out.println(output);
System.exit(0);
} else {
//abnormal...
}
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}

Categories