I have a shell script running through a process runtime on Java.
This Shell Script only stops when you hit CTRL+C
Right now I catch the InputStream from the script in a JTextArea.
but I can't send CTRL+C.
When you run CTRL+C on a Shell Konsole the script stops and sends back information.
and this information is the one that I Can't Catch.
So
How can I Send CTRL+C Through Process runtime?
How can I catch the Inputstream from CTRL+C?
File dirw = new File("/home/mydir/sh/");
Runtime runtime = Runtime.getRuntime();
Process process = null;
process = runtime.exec("./start_test.sh", null, dirw);
OutputStream outp = new OutputStream(process.getOutputStream());
InputStreamReader isr = new InputStreamReader(process.getInputStream());
BufferedReader br = new BufferedReader(isr);
String line = null;
int cont = 1;
while ((line = br.readLine()) != null) {
jtextarea.append("LineOK " + line + "\n");
if( cont == 10) {
outp.write(3); //sending Ctrl+C
outp.flush();
cont =0;
}
cont ++;
}
CTRL+C is a command sent from user to shell. When shell receives it, it sends SIGINT to the foreground process.
To do this in Java use Process.sendSignal(pid, Process.SIGNAL_QUIT) - this only works on Android.
Update: the above command is wrong as it's only available on Android.
The correct way is to send kill -2 pid. Beware: this is UNIX-only solution. Another problem is getting the pid (process id). It turns out there is no OS-agnostic solution to it: How to get PID of process I've just started within java program?
The solution is to resort to OS-dependent hacks as mentioned in the link (getting pid from Process via reflection).
Related
I'm writing a wrapper for the command line based headless server of the game Factorio. I'm using a ProcessBuilder and getting the stdout of the server and am using the rcon connection (long story, stdin wasn't working) to communicate with it. I've gotten it nearly completed up until the point where I was going to package up the jar and run it on the physical server. This is when I noticed that running the jar instead of running from the IDE (Intelij) prevents any output from the server from coming through. I did some more snooping and found that whenever I am using .getInputStream() from the process, the server's output is only sent if the program was launched alongisde the console (java.exe) and doesn't get sent at all if the program was launched without it (javaw.exe).
I tested .getInputStream() with cmd.exe and it works just fine with both java.exe and javaw.exe. I also check to see if the code was following some flow that I wasn't expecting when ran outside of the IDE but it blocks on read.readLine() as if it's waiting for input but not receiving anything.
The process is initialized here:
ProcessBuilder pb = new ProcessBuilder(gameExecutablePath, "--start-server", saveLocation + "\\" + saveName, "--server-settings", serverSettingsLocation + "\\" + serverSettingsName, "-c", serverConfigLocation + "\\" + serverConfigName, "--rcon-port", "" + rconPort, "--rcon-password", "" + rconPasskey);
pb.redirectErrorStream(true);
try
{
myProcess = pb.start();
}catch(Exception e){e.printStackTrace();}
if(myProcess == null) { stop();}
OutputStream serverInput = myProcess.getOutputStream();
write = new BufferedWriter(new OutputStreamWriter(serverInput));
InputStream serverOutput = myProcess.getInputStream();
read = new BufferedReader(new InputStreamReader(serverOutput));
Later, the input is handled by the gui and is sent via rcon (after the server has been fully initialized), and the output is read and printed to the gui's feed, JTextArea, here:
while(serverRunning)
{
try
{
String line = "" + read.readLine();
if(line == null)
{
continue;
}
line = line.trim();
if(line.contains("Opening socket for broadcast"))
{
serverInitialized = true;
initializeRCONConnection();
}
synchronized(serverFeedStringBuilder)
{
printToServerFeed(line);
}
}catch(Exception e){e.printStackTrace();printToServerFeed(e.getMessage());}
}
The application should have printed out the server's output such as all the initializing text about modloading, connecting to the matchmaking server, etc. like it does in the IDE and when java.exe is used. Instead it doesn't output anything at all but the process continues to run and the server can be connected to in-game after it's done initializing.
I am calling a bash scrip script from Java.
The script does the following:
cat /home/user/Downloads/bigtextfile.txt | grep 'hello'
This particular command when run command line takes about 1 second to complete on the text file which is 150MB.
When calling the bash script via Java using the following call:
command = "sh /home/user/bashfiletocall"
p = Runtime.getRuntime().exec(command);
The time to complete takes so long I don't wait.
Am I doing something very wrong and if not can you explain the reason for the huge lack in performance?
NOTE: I was running it in Netbeans and this seems to be the problem .. when I ran the file command line it was quick. The performance between execution in netbeans and command line is huge.
Many thanks.
private String executeCommand(String command) {
StringBuilder output = new StringBuilder();
BufferedReader reader = null;
Process p;
try {
p = Runtime.getRuntime().exec(command);
p.waitFor();
reader = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line = "";
while ((line = reader.readLine())!= null) {
output.append(line + "\n");
}
} catch (Exception e) {
e.printStackTrace();
}
return output.toString();
}
After starting your process you need start reading from the input stream. Otherwise the buffers are running full and p.waitFor() waits forever.
Javadoc of the Process class:
Because some native platforms only provide limited buffer size for standard input and output streams, failure to promptly write the input stream or read the output stream of the subprocess may cause the subprocess to block, or even deadlock.
I would like to have an app (service) that runs in the background, running procrank on a timer & writing the output to a file on the sdcard. Essentially I want to execute "/system/xbin/procrank > /sdcard/procrank.txt". During the wait time I do some [minimal] processing of the file, so I can overwrite it the next time it runs.
This is on a test phone, so it can be considered as "rooted". I have tried many different ways to run the command without success.
When I use this:
String[] cmd = { "/system/xbin/procrank > /sdcard/procrank_result.txt" };
Process process = new ProcessBuilder()
.command(cmd)
.redirectErrorStream(true)
.start();
BufferedReader in = new BufferedReader(
new InputStreamReader(process.getInputStream()));
String line = "";
while ((line = in.readLine()) != null) {
Log.i(Constants.TAG, "Line=" + line);
}
process.waitFor();
I get an IOException:
09-28 02:48:16.307: W/System.err(9579): java.io.IOException: Error running exec(). Command: [/system/xbin/procrank > /sdcard/procrank_result.txt] Working Directory: null Environment: [ANDROID_ROOT=/system, EMULATED_STORAGE_SOURCE=/mnt/shell/emulated, LOOP_MOUNTPOINT=/mnt/obb, ...
So I thought I would just issue the command and read the output:
String[] cmd = { "/system/xbin/procrank" };
Process process = new ProcessBuilder()
.command(cmd)
.redirectErrorStream(true)
.start();
BufferedReader in = new BufferedReader(
new InputStreamReader(process.getInputStream()));
String line = "";
while ((line = in.readLine()) != null) {
Log.i(Constants.TAG, "Line=" + line);
}
process.waitFor();
but I get an error from procrank that I don't understand:
Line=Error creating kernel interface -- does this kernel have pagemap?
Can anyone help me with this?
Also, I have tried many different ways to issue the command, so another variation of using process from getRuntime().exec probably won't work unless it's different from the many other solutions here on StackOverflow.
Yes, my sdcard is writable and /sdcard is equivalent to . I can issue the command from adb shell and it creates the file.
Would I have more luck with trying to connect via socket to localhost and then issuing the command?
Too old a question but if anyone encounters this: I too hit into this issue. Doing the following fixed this for me:
adb wait-for-devices root
adb wait-for-devices remount
adb shell procrank
This appears to be a problem with the build on the phone and perhaps something else.
I don't know how to close this question. Can I close it?
I'm trying to run a Java application which creates a new powershell process on startup and then later on interacts with it multiple times. Calling powershell.exe and have it execute a single command and return the output works fine for me. The problem arises if I don't want the powershell process to immediately finish/exit but to stay open so I can write to its outputStream and receive results back from the inputStream.
String input = "dir";
String[] commandList = {"powershell.exe", "-Command", "dir"};
ProcessBuilder pb = new ProcessBuilder(commandList);
Process p = pb.start();
if(input != null) {
PrintWriter writer = new PrintWriter(new OutputStreamWriter(new BufferedOutputStream(p.getOutputStream())), true);
writer.println(input);
writer.flush();
writer.close();
}
//p.getOutputStream().close();
Gobbler outGobbler = new Gobbler(p.getInputStream());
Gobbler errGobbler = new Gobbler(p.getErrorStream());
Thread outThread = new Thread(outGobbler);
Thread errThread = new Thread(errGobbler);
outThread.start();
errThread.start();
System.out.println("Waiting for the Gobbler threads to join...");
outThread.join();
errThread.join();
System.out.println("Waiting for the process to exit...");
int exitVal = p.waitFor();
System.out.println("\n****************************");
System.out.println("Command: " + "cmd.exe /c dir");
System.out.println("Exit Value = " + exitVal);
List<String> output = outGobbler.getOuput();
input = "";
for(String o: output) {
input += o;
}
System.out.println("Final Output:");
System.out.println(input);
This code returns the result of the "dir" command from a powershell - fine. But as you can see, I'm trying to run a second "dir" command using
PrintWriter writer = new PrintWriter(new OutputStreamWriter(new BufferedOutputStream(p.getOutputStream())), true);
writer.println(input);
writer.flush();
This has no effect whatsoever - no second dir output is shown when I run my code. I've also experimented with a powershell.exe option to open the powershell but not close it immediately:
String[] commandList = {"powershell.exe", "-NoExit", "-Command", "dir"};
But then my code hangs, meaning the Gobbler's who consume the process's inputStream don't read anything - strangely enough: they don't even read the first line - there must be at least some output....
I've also tried to close the process's outputStream after writing the second "dir" command to it - didn't change anything.
Any help is highly appreciated.
Thanks
Kurt
This sounds about right for the nature of a process spun up by another process. I think you're experiencing pretty standard behavior.
This is the key: p.waitFor()
From Java docs:
causes the current thread to wait, if necessary, until the process represented by this Process object has terminated.
You won't be able to receive the PowerShell output stream until it has terminated. When you run with -NoExit it never exits which is why you are experiencing the hang.
If you run ProcExp from Sysinternals you'll be able to see your Java process spin up an child PowerShell process.
So I don't think you'll be able to interact with it like it's a live object in memory.
I have a Java application which for example needs to restart itself (but also needs to start other processes). This is currently done by closing the current application and then start a new instance using a ShutdownHook and a ProcessBuilder.
My problem is now that the new process runs somewhere in the background and does not have its own console window. On windows machines, a new console window can be created using
cmd /c start "windowtitle" java -jar myApp.jar
But this creates 2 processes: the cmd process and the java process started by 'start'. This makes it for example impossible to get the stdout and stderr of the started process, because we only get those streams for the cmd process, not for the one started by the 'start' command.
The very best solution for me would be to reuse the current console window for the new process but this seems somehow impossible to achieve as i did not find any information on how to do this.
Additionally I would appreciate a solution which works on Unix machines.
Seems, you owe to use small console program-starter for java. It must start java and immediately exit.
#include <windows.h>
#include <tchar.h>
int main(int argc, char** argv)
{
STARTUPINFO si = {sizeof(si)};
PROCESS_INFORMATION pi = {};
CreateProcess(NULL,_T("java -jar myApp.jar"),0,0,0,NORMAL_PRIORITY_CLASS,0,0,&si,&pi);
return 0;
}
I do not know if that's what you're looking for, but it might help.
List<String> command = Arrays.asList("java", "-jar", "myApp.jar");
ProcessBuilder builder = new ProcessBuilder(command);
builder.redirectErrorStream(true);
Process process = builder.start();
InputStream is = process.getInputStream();
final InputStreamReader isr = new InputStreamReader(is);
final BufferedReader br = new BufferedReader(isr);
String line = null;
while ((line = br.getLine()) != null) {
System.out.println("child process: " + line);
}
process.waitFor();
The builder.redirectErrorStream(true); will redirect the stderr to stdout and the while loop will write the stdout of the child process to the stdout of your main application. And don't forget to try catch the Streams and Buffers