Why CTRL + M gives an ASCII value of 10 (decimal value). It should actually give 13. I am connecting to Amazon EC2 linux instance through putty. I execute the below program
import java.io.IOException;
import java.io.InputStream;
import java.io.ByteArrayOutputStream;
public class NumbersConsole {
private static String ttyConfig;
public static void main(String[] args) {
try {
setTerminalToCBreak();
int i=0;
while (true) {
//System.out.println( ""+ i++ );
if ( System.in.available() != 0 ) {
int c = System.in.read();
System.out.println(c);
if ( c == 13 ) {
break;
}
}
} // end while
}
catch (IOException e) {
System.err.println("IOException");
}
catch (InterruptedException e) {
System.err.println("InterruptedException");
}
finally {
try {
stty( ttyConfig.trim() );
}
catch (Exception e) {
System.err.println("Exception restoring tty config");
}
}
}
private static void setTerminalToCBreak() throws IOException, InterruptedException {
ttyConfig = stty("-g");
// set the console to be character-buffered instead of line-buffered
stty("-icanon min 1");
// disable character echoing
stty("-echo");
}
/**
* Execute the stty command with the specified arguments
* against the current active terminal.
*/
private static String stty(final String args)
throws IOException, InterruptedException {
String cmd = "stty " + args + " < /dev/tty";
return exec(new String[] {
"sh",
"-c",
cmd
});
}
/**
* Execute the specified command and return the output
* (both stdout and stderr).
*/
private static String exec(final String[] cmd)
throws IOException, InterruptedException {
ByteArrayOutputStream bout = new ByteArrayOutputStream();
Process p = Runtime.getRuntime().exec(cmd);
int c;
InputStream in = p.getInputStream();
while ((c = in.read()) != -1) {
bout.write(c);
}
in = p.getErrorStream();
while ((c = in.read()) != -1) {
bout.write(c);
}
p.waitFor();
String result = new String(bout.toByteArray());
return result;
}
}
and when I give the input as (CTRL + M), I am getting displayed a value of 10. But I am expecting a value of 13. Please let me know if I am missing anything??
The translation of CR to LF is handled by the tty driver. You're calling setTerminalToCBreak(), which manipulates the tty settings (I think it disables the erase, kill, werase, and rprnt special characters).
The icrnl setting, which is enabled by default, causes carriage return (CR) to be translated to newline (LF). Disabling that should let you see CR characters directly. Setting raw mode changes a number of flags, including turning off icrnl. (Figuring out how to do that in Java is left as an exercise.)
But beware of doing this. The Enter or Return key typically sends a CR character. Translating it to LF is what allows it to mark the end of a line. If you turn off that translation, you might break that behavior unless you handle CR yourself.
For more information on tty settings, man tty or follow this link.
My other answer started on totally the wrong page.
stty ("-cooked")
works for me.
Something in the depths of teletype land wants you to have happy little ^Js instead of ^Ms, but cooking the terminal stops it.
$ stty -cooked ; java -cp /tmp NumbersConsole
13
$
Back in the Good Ol' Days, some computers (Commodore, Apple) used ^M (13) for their Return key; some (IBM) used a combination ^M^J; others (Unix) used ^J (10).
Now, in the modern world, it's almost always ^J (although I think Windows code still has some legacy ^M^J stuff under the hood sometimes?)
Related
Thank you for all your help. I tried everything from creating a new Thread. To changing around the way I use the writer.flush() and the writer.newLine(). It seems like the code keeps getting hung up at the while loop, constantly running the Thread.sleep() nonstop. I cannot provide the batch file because it is sensitive information, but the String command variable on top of the code is the path I am using to access the command. Please if you do answer this question please run the code first with test bat file and two input field.
Batch Script:
#ECHO OFF
SET /P _inputname= Please enter an name:
SET /P _inputpassword= Please enter an password:
IF "%_inputpassword%"=="1234" GOTO :they_said_1234
ECHO You entered the wrong password!
pause
GOTO
:they_said_1234
ECHO You entered 1,2,3,4!
pause
Java Code:
import java.io.*;
import java.nio.CharBuffer;
import java.util.ArrayList;
public class command{
public static void main(String[] args) {
//String command="cmd /c d: && cd UPSDATA\\Virtualization Scripts\\EMC ESXi Grab\\EMC-ESXi-GRAB-1.3.7 && GRAB_RUN ";
//String command="cmd /c date";
String command = "cmd /c cd C:\\Users\\HFB2VZN\\Desktop\\folderG";
try {
Process process = Runtime.getRuntime().exec(command);
try (Writer writer = new OutputStreamWriter(process.getOutputStream());
Reader reader = new InputStreamReader(process.getInputStream())) {
CharBuffer buf = CharBuffer.allocate(80);
int tries = 2;
while (process.isAlive()) {
while (reader.ready() && reader.read(buf) > 0) {
//3
System.out.println("buf.flip() ran");
System.out.append(buf.flip());
buf.clear();
}
if (tries-- == 0) {
System.out.println("Destroyed");
process.destroy();
break;
}
//1
writer.write("random");
writer.flush();
while (!reader.ready()) {
//2
System.out.println("while() loop, Thread runs in non stop loop");
Thread.sleep(800);
}
}
}
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
}
}`
Compared to this example, executing a batch file will be much slower and having multiple commands may imply that there’s no available output for some time before the next output is generated. Not considering this may cause the loops get out of sync and after the process terminated, you must not execute the while (!reader.ready()) {…} loop without checking whether the process is still alive.
Since your batch file’s second GOTO lacks a target (it’s likely supposed to branch backwards), this batch file may terminal earlier than intended. Since no-one reads the error channel, this stays unnoticed. That could be the reason for hanging in that loop. Note further, that you are generating one input to the batch file per loop iteration, but have limited the number of iteration to 2 per tries variable. For a batch file expecting three inputs (name, password, pause), that’s too little.
The main problem is, there is no way to detect whether a subprocess is actually waiting for our input. This is what we have to work-around here. But a temporary stopping of producing output does not always imply that the program now waits for input.
I fixed your last GOTO to jump to the beginning of the batch file and used the following code to make two attempts entering the right password on the second.
try {
Process process = Runtime.getRuntime().exec(command);
try(Writer writer = new OutputStreamWriter(process.getOutputStream());
Reader reader = new InputStreamReader(process.getInputStream())) {
CharBuffer buf = CharBuffer.allocate(80);
int tries = 2;
while(process.isAlive()) {
do {
if(!buf.hasRemaining()) {
buf.flip();
buf = CharBuffer.allocate(buf.capacity()*2).put(buf);
}
do {} while(reader.ready() && reader.read(buf) > 0);
if(buf.position() > 0) {
char c = buf.get(buf.position()-1);
if(c==':' || c=='.') break;
}
long deadLine = System.nanoTime() + TimeUnit.SECONDS.toNanos(1);
for(long remaining = 1;
!reader.ready() && process.isAlive() && remaining > 0;
remaining = deadLine - System.nanoTime()) {
LockSupport.parkNanos(Math.min(
TimeUnit.MILLISECONDS.toNanos(100),
remaining));
}
} while(reader.ready());
String input = buf.flip().toString();
buf.clear();
System.out.print(input);
String value;
if(input.endsWith("name:")) value = "aName";
else if(input.endsWith("password:")) value = tries>1? "wrongPassword": "1234";
else {
value = "<any key>";
tries--;
}
System.out.println("<- "+value);
writer.write(value);
writer.flush();
if(tries == 0) {
System.out.println("Destroying");
process.destroy();
break;
}
}
}
} catch(IOException e) {
e.printStackTrace();
}
Of course, if you can’t fix the GOTO statement, you have to provide the right password at the first attempt.
The code above will wait up to one second for the availability of new output, unless it recognizes one of the expected prompts in the output. And it won’t wait when the process is not alive anymore.
I am creating a custom shell in Java. I have added history to it so that when up arrow is pressed it goes to the previous command, but the up arrow seems to not be working
Here is my code:
public class MyShell {
public static class JavaStringHistory
{
private List<String> history = new ArrayList<String>();
}
public static void main(String[] args) throws java.io.IOException {
JavaStringHistory javaStringHistory = new JavaStringHistory();
javaStringHistory.history.add("");
Integer indexOfHistory = 0;
String commandLine;
BufferedReader console = new BufferedReader
(new InputStreamReader(System.in));
//Break with Ctrl+C
while (true) {
//read the command
System.out.print("Shell>");
commandLine = console.readLine();
javaStringHistory.history.add(commandLine);
//if just a return, loop
if (commandLine.equals(""))
continue;
//history
if (commandLine.equals(KeyEvent.VK_UP))
{
System.out.println("up arrow");
}
//help command
if (commandLine.equals("help"))
{
System.out.println();
System.out.println();
System.out.println("Welcome to the shell");
System.out.println("Written by: Alex Frieden");
System.out.println("--------------------");
System.out.println();
System.out.println("Commands to use:");
System.out.println("1) cat");
System.out.println("2) exit");
System.out.println("3) clear");
System.out.println();
System.out.println();
System.out.println("---------------------");
System.out.println();
}
if (commandLine.equals("clear"))
{
for(int cls = 0; cls < 10; cls++ )
{
System.out.println();
}
}
if(commandLine.startsWith("cat"))
{
System.out.println("test");
//ProcessBuilder pb = new ProcessBuilder();
//pb = new ProcessBuilder(commandLine);
}
else
{
System.out.println("Incorrect Command");
}
if (commandLine.equals("exit"))
{
System.out.println("...Terminating the Virtual Machine");
System.out.println("...Done");
System.out.println("Please Close manually with Options > Close");
System.exit(0);
}
indexOfHistory++;
}
}
}
All I am getting is
Shell>^[[A
Incorrect Command
Shell>
Any thoughts?
There are several problems with your approach:
User blackSmith has mentioned before me that system console handling is platform-dependent when it comes to cursor key handling and similar topics.
BufferedReader.readLine is not a smart choice to use for history cycling in a shell because you want the shell to immediately react to cursor keys and not force the user to press Return or Enter. Reading whole lines is only required for user commands. Thus, you need to scan the keyboard input for each single character or key code and decide by yourself if it is e.g. a cursor key (up/down for history cycling, left/right for cursor movement within the command line) or delete/backspace for command line editing and so forth.
The text strings which are created by reading control characters via readLine can depend on the OS, maybe even on the shell and the character set (UTF-8, ISO-8859-1, US ASCII etc.) on the console.
Built-in shell editing functions like command history might get in the way with readLine, e.g. on Linux I see the "^[[A" stuff for cursor up, on Windows the cursor keys are passed through to the built-in command history feature of cmd.exe. I.e. you need to put the console in raw mode (line editing bypassed and no Enter key required) as opposed to cooked mode (line editing with Enter key required).
Anyway, so as to answer your initial question about how to find out which key codes are produced by BufferedReader.readLine, it is actually quite simple. Just dump the bytes to the console like so:
commandLine = console.readLine();
System.out.println("Entered command text: " + commandLine);
System.out.print ("Entered command bytes: ");
for (byte b : commandLine.getBytes())
System.out.print(b + ", ");
System.out.println();
Under Linux cursor up might be something like "27, 91, 65" or just "91, 65", depending on the terminal. cursor down ends with "66" instead on my system. So you could do something like:
public class MyShell {
private static final String UP_ARROW_1 = new String(new byte[] {91, 65});
private static final String UP_ARROW_2 = new String(new byte[] {27, 91, 65});
private static final String DN_ARROW_1 = new String(new byte[] {91, 66});
private static final String DN_ARROW_2 = new String(new byte[] {27, 91, 66});
// (...)
public static void main(String[] args) throws IOException {
// (...)
// history
else if (commandLine.startsWith(UP_ARROW_1) || commandLine.startsWith(UP_ARROW_2)) {
System.out.println("up arrow");
}
else if (commandLine.startsWith(DN_ARROW_1) || commandLine.startsWith(DN_ARROW_2)) {
System.out.println("down arrow");
}
// (...)
}
}
But all this is just for explanation or demonstration and so as to answer your question - I do like to get the bounty. ;-)
Maybe a way to go is not to re-invent the wheel and use the work of others, e.g. a framework like JLine. It is not perfect either from what I have heard, but goes way further than anything you can develop by yourself in a short time. Someone has written a short introductory blog post about JLine. The library seems to do just what you need. Enjoy!
Update: I gave JLine 2.11 a little try with this code sample (basically the one from the blog post plus tab filename completion:
import java.io.IOException;
import jline.TerminalFactory;
import jline.console.ConsoleReader;
import jline.console.completer.FileNameCompleter;
public class MyJLineShell {
public static void main(String[] args) {
try {
ConsoleReader console = new ConsoleReader();
console.addCompleter(new FileNameCompleter());
console.setPrompt("prompt> ");
String line = null;
while ((line = console.readLine()) != null) {
console.println(line);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
TerminalFactory.get().restore();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
It works nicely on Windows and Linux, but for me tab completion only works on Linux, not on Windows. Anyway, command history works well on both platforms.
VK_UP is an integer constant, while in.readLine() is a string.
They won't equal each other. Why don't you try to test for the code that appears in console usually when you click up arrow? So like:
if (in.readLine().equals("^[[A"))
and then you could clear the line, and write the command in the arraylist with the highest index.
Also, I tested this and found a bug. Change your if statements besides the first to else if; after any command it will eventually get to the else and display "Incorrect Command"
I'm trying to implement an Android application to connect to the open source software Motion. The goal is to be able to check the status of the application and get the last image captured.
I do not program in Java very much, my background is principally in C and Python. I've not had any real issues with understanding the UI part of Android, but I've found it to be incredibly painful to work with any sort of byte buffer. The Motion software has an HTTP API that is very simple. Opening the URL connection is easy in Java. The response from the default page looks like this
Motion 3.2.12 Running [4] Threads
0
1
2
3
For my purposes the first thing the application needs to do it parse out the number of threads. At some point I can also retrieve the version number from the first line, but that's not really important presently.
Here's my code
package com.hydrogen18.motionsurveillanceviewer;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.List;
public class MotionHttpApi {
String host;
int port = 80;
boolean secure = false;
int numberOfThreads = -1;
String getBaseUrl()
{
StringBuilder sb = new StringBuilder();
sb.append(secure ? "https://" : "http://");
sb.append(host);
sb.append(':');
sb.append(port);
return sb.toString();
}
public int getNumberOfCameras() throws IOException
{
if(numberOfThreads == -1)
{
retrieveSplash();
}
if(numberOfThreads == 1)
{
return 1;
}
return numberOfThreads - 1;
}
void retrieveSplash () throws IOException
{
URL url = new URL(getBaseUrl());
HttpURLConnection conn = (HttpURLConnection)url.openConnection();
if(conn.getResponseCode()!=HttpURLConnection.HTTP_OK)
{
throw new IOException("Got response code" + conn.getResponseCode());
}
try{
Byte[] buffer = new Byte[512];
byte[] sbuf = new byte[128];
int offset = 0;
InputStream in = new BufferedInputStream(conn.getInputStream());
boolean foundInfoString= false;
while( ! foundInfoString)
{
//Check to make sure we have not run out of space
if(offset == buffer.length)
{
throw new IOException("Response too large");
}
//Read into the smaller buffer since InputStream
//can't write to a Byte[]
final int result = in.read(sbuf,0,sbuf.length);
//Copy the data into the larger buffer
for(int i = 0; i < result;++i)
{
buffer[offset+i] = sbuf[i];
}
//Add to the offset
offset+=result;
//Wrap the array as a list
List<Byte> list = java.util.Arrays.asList(buffer);
//Find newline character
final int index = list.indexOf((byte) '\n');
//If the newline is present, extract the number of threads
if (index != -1)
{
//Find the number of threads
//Thread number is in the first lin like "[X]"
final int start = list.indexOf((byte)'[');
final int end = list.indexOf((byte)']');
//Sanity check the bounds
if(! (end > start))
{
throw new IOException("Couldn't locate number of threads");
}
//Create a string from the Byte[] array subset
StringBuilder sb = new StringBuilder();
for(int i = start+1; i != end; ++i)
{
final char c = (char) buffer[i].byteValue();
sb.append(c);
}
String numThreadsStr = sb.toString();
//Try and parse the string into a number
try
{
this.numberOfThreads = Integer.valueOf(numThreadsStr);
}catch(NumberFormatException e)
{
throw new IOException("Number of threads is NaN",e);
}
//No more values to extract
foundInfoString = true;
}
//If the InputStream got EOF and the into string has not been found
//Then an error has occurred.
if(result == -1 && ! foundInfoString )
{
throw new IOException("Never got info string");
}
}
}finally
{
//Close the connection
conn.disconnect();
}
}
public MotionHttpApi(String host,int port)
{
this.host = host;
this.port = port;
}
}
The code works just fine when you call getNumberOfCameras(). But I think I must not be really understandings omething in terms of java, because the retrieveSplash method is far too complex. I could do the same thing in just 10 or so lines of C or 1 line of Python. Surely there must be a saner way to manipulate bytes in java?
I think there are some style issues, like I probably should not be throwing IOException whenever the integer fails to parse. But that's a separate issue.
Read the first line as Gautam Tandon suggested and then use a regex.
You can then check if the regex matches and even easily extract the number.
Regex' can be created at http://txt2re.com. I've already done that for you.
The page even creates Java, Pyhton, C, etc. files for you to work with.
// URL that generated this code:
// http://txt2re.com/index-java.php3?s=Motion%203.2.12%20Running%20[4]%20Threads&-7&-19&-5&-20&-1&2&-22&-21&-62&-63&15
import java.util.regex.*;
class Main
{
public static void main(String[] args)
{
String txt="Motion 3.2.12 Running [4] Threads";
String re1="(Motion)"; // Word 1
String re2="( )"; // White Space 1
String re3="(3\\.2\\.12)"; // MMDDYY 1
String re4="( )"; // White Space 2
String re5="(Running)"; // Word 2
String re6="( )"; // White Space 3
String re7="(\\[)"; // Any Single Character 1
String re8="(\\d+)"; // Integer Number 1
String re9="(\\])"; // Any Single Character 2
String re10="( )"; // White Space 4
String re11="((?:[a-z][a-z]+))"; // Word 3
Pattern p = Pattern.compile(re1+re2+re3+re4+re5+re6+re7+re8+re9+re10+re11,Pattern.CASE_INSENSITIVE | Pattern.DOTALL);
Matcher m = p.matcher(txt);
if (m.find())
{
String word1=m.group(1);
String ws1=m.group(2);
String mmddyy1=m.group(3);
String ws2=m.group(4);
String word2=m.group(5);
String ws3=m.group(6);
String c1=m.group(7);
String int1=m.group(8);
String c2=m.group(9);
String ws4=m.group(10);
String word3=m.group(11);
System.out.print("("+word1.toString()+")"+"("+ws1.toString()+")"+"("+mmddyy1.toString()+")"+"("+ws2.toString()+")"+"("+word2.toString()+")"+"("+ws3.toString()+")"+"("+c1.toString()+")"+"("+int1.toString()+")"+"("+c2.toString()+")"+"("+ws4.toString()+")"+"("+word3.toString()+")"+"\n");
}
}
}
//-----
// This code is for use with Sun's Java VM - see http://java.sun.com/ for downloads.
//
// Paste the code into a new java application or a file called 'Main.java'
//
// Compile and run in Unix using:
// # javac Main.java
// # java Main
//
String int1=m.group(8); gives you the desired integer. Of course you can simplify the above code. It's way to verbose right now.
You can simplify the retrieveSplash method considerably by using BufferedReader. Here's a simpler version of your function:
void retrieveSplash_simpler() throws IOException {
URL url = new URL(getBaseUrl());
HttpURLConnection conn = (HttpURLConnection)url.openConnection();
// open the connection
conn.connect();
// create a buffered reader to read the input stream line by line
BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
// find number of threads
String firstLine = reader.readLine();
int x = firstLine.indexOf("[");
int y = firstLine.indexOf("]");
if (x > 0 && y > 0 && x < y) {
try {
numberOfThreads = Integer.parseInt(firstLine.substring(x+1, y));
} catch (NumberFormatException nfe) {
// disconnect and throw exception
conn.disconnect();
throw new IOException("Couldn't locate number of threads");
}
} else {
// disconnect and throw exception
conn.disconnect();
throw new IOException("Couldn't locate number of threads");
}
// disconnect
conn.disconnect();
}
I'd further clean up the above method by using try/catch/finally blocks at the appropriate places so that I don't have to duplicate that "conn.disconnect()". But I didn't do that here to keep it simple (try/catch/finally do become tricky sometimes...).
I am a total newbie in JSPs/Tomcat and to a large extent in Java as well. Here's what I have to do -- when a user clicks a button/visits a URL, I want to launch a Java program (which takes some command line arguments).
I can very easily do
Runtime.exec("C:\\Python27\\python.exe test.py")
OR
Runtime.exec("java -cp %CLASSPATH%;C:\\devprojects HelloWorld"
and this works fine. Where HelloWorld.class just prints "HelloWorld".
However, when I attempt a java program which takes command line arguments, the GET request just hangs doing nothing. I don't know what logs to look for or what could be wrong here. After having spent TWO days on trying various things, I am just about to give up now.
Runtime.exec("java -cp %CLASSPATH%;C:\\devprojects Run --username Blah --password Foo");
What user does Tomcat end up running this java program as? Can I make it to be Administrator? This is on Windows 2008, does UAC interfere with things?
I cannot modify the Run.class here, I HAVE to run it as is and with command line parameters.
Please advise.
One idea: you are relying on the default tokenization of your command line as one complete String, and it is not parsing the last one as you expect. Instead you should use the form of this method that takes a String[], after you have chopped up the command line yourself:
http://docs.oracle.com/javase/6/docs/api/java/lang/Runtime.html#exec(java.lang.String[])
Or, it is waiting for input from you, or waiting for you to read its output. This could explain the hang. Search the internet for the dangers of streams and Runtime.exec().
Consider ProcessBuilder instead.
Remember also that you have to be sure that the executed file dont run "forever", and
if you need to pass some arguments, you could use this:
static String startProcess(String command, String dir) throws IOException {
StringBuffer ret = new StringBuffer();
String[] comm = new String[3];
comm[0] = COMMAND_INTERPRETER[0];
comm[1] = COMMAND_INTERPRETER[1];
comm[2] = command;
long start = System.currentTimeMillis();
try {
//Start process
Process ls_proc = Runtime.getRuntime().exec(comm, null, new File(dir));
//Get input and error streams
BufferedInputStream ls_in = new BufferedInputStream(ls_proc.getInputStream());
BufferedInputStream ls_err = new BufferedInputStream(ls_proc.getErrorStream());
boolean end = false;
while (!end) {
int c = 0;
while ((ls_err.available() > 0) && (++c <= 1000)) {
ret.append(conv2Html(ls_err.read()));
}
c = 0;
while ((ls_in.available() > 0) && (++c <= 1000)) {
ret.append(conv2Html(ls_in.read()));
}
try {
ls_proc.exitValue();
//if the process has not finished, an exception is thrown
//else
while (ls_err.available() > 0)
ret.append(conv2Html(ls_err.read()));
while (ls_in.available() > 0)
ret.append(conv2Html(ls_in.read()));
end = true;
}
catch (IllegalThreadStateException ex) {
//Process is running
}
//The process is not allowed to run longer than given time.
if (System.currentTimeMillis() - start > MAX_PROCESS_RUNNING_TIME)
//this is very important
{
ls_proc.destroy();
end = true;
ret.append("!!!! Process has timed out, destroyed !!!!!");
}
try {
Thread.sleep(50);
}
catch (InterruptedException ie) {}
}
}
catch (IOException e) {
ret.append("Error: " + e);
}
return ret.toString();
}
I'm trying to create a Thread that keeps netsh windows command-line tool open so I can execute netsh commands without open it every single time.
The thing is, once I've created the Thread, just the first command call works... the subsequent calls seems to have no effect.
Here is my code:
public class NetshThread implements Runnable{
private static Process netshProcess = null;
private static BufferedInputStream netshInStream = null;
private static BufferedOutputStream netshOutStream = null;
public BufferedReader inPipe = null;
public void run(){
startNetsh();
}
public void startNetsh(){
try {
netshProcess = Runtime.getRuntime().exec("netsh");
netshInStream = new BufferedInputStream(netshProcess.getInputStream());
netshOutStream = new BufferedOutputStream(netshProcess.getOutputStream());
inPipe = new BufferedReader(new InputStreamReader(netshInStream));
} catch (IOException e) {
e.printStackTrace();
}
}
public void executeCommand(String command){
System.out.println("Executing: " + command);
try {
String str = "";
netshOutStream.write(command.getBytes());
netshOutStream.close();
while ((str = inPipe.readLine()) != null) {
System.out.println(str);
}
} catch (IOException e) {
e.printStackTrace();
}
}
public void closeNetsh(){
executeCommand("exit");
}
public static void main(String[] args){
NetshThread nthread = new NetshThread();
nthread.run();
String command = "int ip set address " +
"\"Local Area Connection 6\" static .69.69.69 255.255.255.0";
nthread.executeCommand(command);
command = "int ip set address " +
"\"Local Area Connection 6\" static 69.69.69.69 255.255.255.0";
nthread.executeCommand(command);
System.out.println("*** DONE ***");
}
}
Thank you!!! =)
Update 1:
Ok... I'm now using a PrintWriter instead... so I think I don't need to flush anything anymore, since the constructor is:
new PrintWriter(netshOutStream, true); (just like Mr. Shiny told me)...
Suppose I decide to break the while loop when the first output line is available... I doesn't work either... the next command wont be executed.... My code now looks like:
import java.io.*;
public class NetshThread implements Runnable{
private static Process netshProcess = null;
private static BufferedInputStream netshInStream = null;
private static BufferedOutputStream netshOutStream = null;
public BufferedReader inPipe = null;
private PrintWriter netshWriter = null;
public void run(){
startNetsh();
}
public void startNetsh(){
try {
netshProcess = Runtime.getRuntime().exec("netsh");
netshInStream = new BufferedInputStream(netshProcess.getInputStream());
netshOutStream = new BufferedOutputStream(netshProcess.getOutputStream());
netshWriter = new PrintWriter(netshOutStream, true);
inPipe = new BufferedReader(new InputStreamReader(netshInStream));
} catch (IOException e) {
e.printStackTrace();
}
}
public void executeCommand(String command){
System.out.println("Executing: " + command);
try {
String str = "";
netshWriter.println(command);
while ((str = inPipe.readLine()) != null) {
System.out.println(str);
break;
}
} catch (IOException e) {
e.printStackTrace();
}
}
public void closeNetsh(){
executeCommand("exit");
}
public static void main(String[] args){
NetshThread nthread = new NetshThread();
Thread xs = new Thread(nthread);
xs.run();
String command = "int ip set address " +
"\"Local Area Connection 6\" static .69.69.69 255.255.255.0";
nthread.executeCommand(command);
command = "int ip set address " +
"\"Local Area Connection 6\" static 69.69.69.69 255.255.255.0";
nthread.executeCommand(command);
System.out.println("*** DONE ***");
}
}
and the output I get:
Executing: int ip set address "Local
Area Connection 6" static .69.69.69
255.255.255.0 netsh>.69.69.69 is not an acceptable value for addr.
Executing: int ip set address "Local
Area Connection 6" static 69.69.69.69
Why the second command is not executed???
255.255.255.0
* DONE *
Update 2:
Everything seemed to work just fine until a teacher tried my app in a spanish-windows enviroment....
my code looks like this:
Scanner fi = new Scanner(netshProcess.getInputStream());
public void executeCommand(String command) {
System.out.println("Executing: " + command);
String str = "";
netshWriter.println(command);
fi.skip("\\s*");
str = fi.nextLine();
System.out.println(str);
}
and what i need is to somehow set the netshWriter encoding to the windows default.
Can anyone know who to do this?
You are closing the output stream.
You need to move the stream processing into separate threads. What's happening is that inPipe.readLine() is blocking waiting for netsh to return data. Apache has a package that deals with process handling. I'd look at using that instead of rolling your own (http://commons.apache.org/exec/)
This seems wrong in many ways.
First, why a Runnable object? This isn't ever passed to a Thread anywhere. The only thread you're creating isn't a java thread, it is an OS process created by exec().
Second, you need a way to know when netsh is done. Your loop that reads the output of netsh will just run forever because readLine will only return null when netsh closes its standard out (which is never, in your case). You need to look for some standard thing that netsh prints when it is done processing your request.
And as others mentioned, close is bad. Use a flush. And hope netsh uses a flush back to you...
I'd try:
PrintWriter netshWriter = new PrintWriter(netshOutputStream, true); // auto-flush writer
netshWriter.println(command);
No close()ing the stream, flush the stream automatically, and uses a writer to send character data rather than relying on the platforms "native character set".
You do definitely need to remove the close, else you'll never be able to execute another command. When you say "it won't work" once the close() call removed, do you mean no commands are processed?
Chances are that after you send the bytes for the command, you need to send some kind of confirmation key for the process to start, well, processing it. If you'd normally enter this from the keyboard it might be as simple as a carriage return, otherwise it might need to be a Ctrl-D or similar.
I'd try replacing the close() line with
netshOutStream.write('\n');
and see if that works. Depending on the software you might need to change the character(s) you send to signify the end of the command, but this general approach should see you through.
EDIT:
It would also be prudent to call
netshOutStream.flush();
after the above lines; without the flush there's no guarantee that your data will be written and in fact, since you're using a BufferedInputStream I'm 99% sure that nothing will be written until the stream is flushed. Hence why the code afterwards blocks, as you're waiting for a response while the process has not seen any input yet either and is waiting for you to send it some.
I've used scanner instead of BufferedReader, just because I like it. So this code works:
Scanner fi = new Scanner(netshProcess.getInputStream());
public void executeCommand(String command) {
System.out.println("Executing: " + command);
String str = "";
netshWriter.println(command);
fi.skip("\\s*");
str = fi.nextLine();
System.out.println(str);
}
It executes both commands.