Linux shell commands behave strange with Runtime.exec() (screen command) - java

I am sitting here since at least allways on a problem that really beats me down!
I'm writing on a simple and small java program that easily passes any command to a (linux) screen session.
Shell command: bash -c "screen -p 0 -S sessionname -X eval 'stuff \"some command\"\015'"
When I enter this in the command shell, it all works fine!
But if I let a Java programm do this, nothing happens at all! Not even a message or error or hint! Simply nothing!
I let the program echo the generated screen command and if I take that output and paste it into the shell, it works.
Here is my Java code:
static public void screenCmd() throws IOException
{
String command = "bash -c \"screen -p 0 -S screenname -X eval 'stuff \\\"cmd\\\"\\015'\"";
System.out.println("debug: '" + command + "'"); //output would work
//when copy and paste it to the shell
InputStreamReader isr = new InputStreamReader(
Runtime.getRuntime().exec(command).getInputStream()
);
//for debug output
BufferedReader br = new BufferedReader(isr);
String line = "";
while ((line = br.readLine()) != null)
System.out.println(line);
}
The funniest thing is, that all the other shell commands are working.
I tried Java to exec tail, whomi, cp, ls, ... without any problem. Why not screen?
The Java program is started by the same user who started the screen session.
If somebody has an even small idea please report! I am driving insane here!
p.s.: The screen session also runs a java program! (could that be part of the problem?)
Thanks.

I'd recommend that you try the more modern ProcessBuilder class.
And read these:
Five Common java.lang.Process Pitfalls
When Runtime.exec() won't
From Runtime.exec() to ProcessBuilder

The real problem is the way the command line is parsed: Java's Runtime.exec doesn't follow the same quoting rules as Bash. You should use the array version of the API instead:
String[] command = {"bash", "-c",
"screen -p 0 -S screenname -X eval 'stuff \\\"cmd\\\"\\015'"};
Runtime.getRuntime().exec(command);
You can reduce some of the quoting mess if you exec screen directly without going through bash:
String[] command = {"screen", "-p", "0",
"-S", "10624.pts-2.koivu", "-X", "eval", "stuff \"cmd\"\\015"};

Related

Having a hard time with linux command calls in Java with exec()

I'm trying to use Runtime.getRuntime().exec() to call a program as if it was called from the terminal, but it just crashes with a fatal error after reading the first file.
In the terminal I run the command like so:
mace4 -c -f inputFile.in > outputFile.out
It works as expected, reading from the first file and outputting in the second one.
In Java I try to run it this way:
String args[] = new String[]{"mace4", "-c", "-f", inputFileName ,">",outputFileName};
try {
String s;
Process proc = Runtime.getRuntime().exec(args, null, new File("/home/user/workDirectory/"));
BufferedReader br = new BufferedReader(
new InputStreamReader(proc.getInputStream()));
while ((s = br.readLine()) != null)
System.out.println("line: " + s);
proc.waitFor();
proc.destroy();
As soon as the program reaches the end of the first file, it throws this:
Fatal error: read_all_input, file > not found
The program is quite old and I can't seem to find a way to get a more detailed error out of it..
I tried calling it with these arguments {"sh or bash", "-c", "mace4", "-c", "-f", inputFileName ,">",outputFileName} which makes the program run and then freeze (or at least nothing appears in the console)..
Am I calling the terminal command wrong and if yes what should I change?
PS: this is my first question here, if I missed anything, I'm sorry..
It looks like you're trying to use the Bash output redirection operator >. This redirects the output of the program you're running to a file (or another program)
This answer explains how to do this using ProcessBuilder which should work for what you're trying to do here.
For example:
ProcessBuilder pb = new ProcessBuilder("mace4", "-c", "-f", inputFileName);
pb.redirectOutput(new File(outputFileName));
Process p = pb.start();

complete CMD command not running from java

I am running the below query through Java on a Postgres DB using psql:
psql.exe -U <user> -w -h <host> -d <db_name> -a -f <file> 2> "<path_to_file>\psql.log"
Initially, for quite some time the java program did create the file. Then I ran into another problem, that it was not overwriting the log file. So i used file.delete() function after every time this log file got created via java.
Now, Java is not even creating the log file for some reason. If I run the above manually in command prompt, it runs absolutely fine, but not via java code. I can see this command getting run in the java log, but it does not create the log file even when i have removed the file.delete() function
I researched a lot on it but could not find any solution. Any help would be highly appreciated.
its a long code..so i will tell you the relevant part.
I am calling a function from a thread. Code is below for that function:
public static void SaveACopyfileToServer(int auditid,String filepath,String fname,String tb_name,String plpgsql_path) throws Exception
{
Map<String, String> env = System.getenv();
String plpgsql = "\""+plpgsql_path+"\" -U "+env.get("PG_USER")+" -w -h "+env.get("PG_HOST")+" -d "+env.get("PG_DB")+" -a -f "+"\""+filepath+"copy_"+tb_name+auditid+".sql\" 2> \"C:\\ER\\ETL\\logs\\psql.log\"";
System.out.println(plpgsql);
Process p = Runtime.getRuntime().exec(plpgsql);
p.getOutputStream().close();
p.waitFor();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS");
Calendar cal10 = Calendar.getInstance();
System.out.println("Data loaded for "+tb_name+auditid+" at "+sdf.format(cal10.getTime()));
}
After this i am calling another function which is:
public static void extracterrorreason(String fname,int auditid,String sessionid,Connection con_pg) throws FileNotFoundException, IOException, InterruptedException{
File file = new File("C:\\ER\\ETL\\logs\\psql.log");
if(file.exists())
{
System.out.println("File present");
}
else
{
System.out.println(file+" not found");
}
if (file.length()!=0){
System.out.println("Log file being read is "+file);
BufferedReader br = new BufferedReader(new FileReader(file));
String line = br.readLine();
String out_err = line.substring(line.indexOf("ERROR"));
System.out.println(out_err);
System.out.println("Error while loading the file into Database for file "+fname);
String comment = "CopyToStage','"+out_err;
Utils.updateAuditDetailTable(auditid, sessionid, -1, comment, true, con_pg,"");
br.close();
//file.delete();
}
}
The first function used to create the psql.log file, but now it does not even create it. Not sure where is the problem. Every time i run the code and from the second function,i get the printline that log file not found. The part before the redirection of the output of the cmd command works fine.
I tried process builder also..
I even tried it with Process builder
String plpgsql = "\""+plpgsql_path+"\" -U "+env.get("PG_USER")+" -w -h "+env.get("PG_HOST")+" -d "+env.get("PG_DB")+" -a -f "+"\""+filepath+"copy_"+tb_name+auditid+".sql\" 2> \"C:\\ER\\ETL\\psql_" +auditid +".log\"";
ProcessBuilder pb = new ProcessBuilder("cmd.exe",plpgsql);
Process p =pb.start();
p.getOutputStream().close();
p.waitFor();
I expect that the problem is that Runtime.getRuntime().exec(plpgsql) is splitting the command line into arguments incorrectly. Basically, exec does not understand quoting. Instead, it splits wherever it sees one or more spaces ... even if those spaces are in quotes.
The solution is to use the exec(String[]) overload, and pass each individual argument as a separate string; e.g.
.exec(new String[]{plpgsql_path,
"-U",
env.get("PG_USER"),
"-w,
"-h",
// etcetera
});
UPDATE
I didn't notice that you were using > output redirection as well1.
That doesn't work with exec either. (And the same applies to all shell syntax.) To get redirection, you need to use ProcessBuilder and one of the redirect methods.
The other alternative is to run the command in a shell. Pass the command as a string, and let the shell take care of the quote handling, substitution of environment variables, globbing, redirection ... and so on.
For example (if you were running on UNIX, Linux or MacOSX):
.exec(new String[]{"/bin/sh", "-c", plpgsql});
For Windows
.exec(new String[]{"cmd.exe", "/C", plpgsql});
Note the "/C" option in the Windows case!
1 - It serves you right for not line-breaking that ~200 character line in your source code! Check out what Java coding standards say about source line lengths ...

Run consecutive Commands Linux with java runtime exec

I need to run two commands Linux using java code like this:
Runtime rt = Runtime.getRuntime();
Process pr=rt.exec("su - test");
String line=null;
BufferedReader input = new BufferedReader(new InputStreamReader(pr.getInputStream()));
while((line=input.readLine()) != null) {
System.out.println(line);
}
pr = rt.exec("whoami");
input = new BufferedReader(new InputStreamReader(pr.getInputStream()));
line=null;
while((line=input.readLine()) != null) {
System.out.println(line);
}
int exitVal = pr.waitFor();
System.out.println("Exited with error code "+exitVal);
} catch(Exception e) {
System.out.println(e.toString());
e.printStackTrace();
}
The problem is the output of the second command ("whoami") doesn't display the current user which used on the first command ("su - test")!
Is there any problem on this code please?
In the general case, you need to run the commands in a shell. Something like this:
Process pr = rt.exec(new String[]{"/bin/sh", "-c", "cd /tmp ; ls"});
But in this case that's not going to work, because su is itself creating an interactive subshell. You can do this though:
Process pr = rt.exec(new String[]{"su", "-c", "whoami", "-", "test"});
or
Process pr = rt.exec(new String[]{"su", "test", "-c", "whoami"});
Another alternative is to use sudo instead of su; e.g.
Process pr = rt.exec(new String[]{"sudo", "-u", "test", "whoami"});
Note: while none of the above actually require this, it is a good idea to assemble the "command line" as an array of Strings, rather than getting exec to do the "parsing". (The problem is that execs splitter does not understand shell quoting.)
As stated in the Javadoc for Runtime.exec():
Executes the specified string command in a separate process.
each time you execute a command via exec() it will be executed in a separate subprocess. This also means that the effect of su ceases to exist immediately upon return, and that's why the whoami command will be executed in another subprocess, again using the user that initially launched the program.
su test -c whoami
will give you the result you want.
If you want to run multiple commands in a way the commands would execute in a subshell if need be see the response here
How can I run multiple commands in just one cmd windows in Java? (using ProcessBuilder to simulate a shell)

Getting error in calling shell script in windows environment using java code and cygwin...!

I want to call shell script on windows environment using java code.
I am trying to execute code below to run my test script(not actual script):
Java Code:
public static void main (String args[]) {
Runtime r = Runtime.getRuntime();
try {
Process p = r.exec("C:\\cygwin\\bin\\bash -c '/cygdrive/d/scripts/test.sh'");
InputStream in = p.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(in));
System.out.println("OUT:");
String line;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
in = p.getErrorStream();
br = new BufferedReader(new InputStreamReader(in));
System.out.println("ERR:");
while ((line = br.readLine()) != null) {
System.out.println(line);
}
p.waitFor();
} catch (Exception e) {
e.printStackTrace();
}
}
test.sh
#!/bin/bash
rm -rf ./test
But I am getting these error and script in not able to remove the directory.
ERR:
/cygdrive/d/ereader/scripts/test.sh: line 2: $'\r': command not found
/cygdrive/d/ereader/scripts/test.sh: line 3: rm: command not found
Another thing, when I run the script from the cygwin terminal it works fine. I checked the path variable they all are fine. But I try to execute same script through java code it gives error..
Now how to tell java program where to refer for rm commands?
The giveaway is the '\r' error.
Windows and Unix (which includes Mac and Linux) use different representations of a new line. Windows uses '\r\n' while Unix simply uses '\n'. Most programming editors account for this and only insert a '\n' so they work with Unix tools.
I would suggest retyping your shell script in another editor like Notepad++ (which only inserts '\n'), or make sure your current editor is set to use Unix newlines. You have to retype it or the bad '\r\n' sequences will get copied over. You might have some luck in doing a replace-all but that always acts flaky for me.
probably you will need to set the PATH variable first thing in the test.sh script. Make sure rm is in the PATH you set
Not really answer. You could try following to narrow down the problem
Use ProcessBuilder instead of Process. It is much more friendly to handle arguments.
Set absolute path to rm (/bin/rm ) in the script
remove using absolute path to directory or remove after verifying you are in the correct directory..
The bash prompt you have is result of cygwin.bat calling bash with --login. It will have path variables and other usesul stuff sources. bash -c does not do it.
Try launching bash.exe with bash -l -c <command> : This sources the bash profile.

Run linux command through java.

I want to run nm command in linux through java.
I tried this code :
command = "nm -l file1.o > file1.txt";
Process p = Runtime.getRuntime().exec(command);
But it's not working, what is wrong with the code?
That is not an executable, it is in fact a shell script.
If you invoke the shell with -c, then you can execute your command:
/bin/sh -c "command > here"
Here's what you need to do:
String command = "nm -l file1.o > file1.txt";
Process p = Runtime.getRuntime().exec(new String[]{"/bin/sh", "-c", command});
The following "simple answer" WON'T WORK :
String command = "/bin/sh -c 'nm -l file1.o > file1.txt'";
Process p = Runtime.getRuntime().exec(command);
because the exec(String) method splits its the string naively using whitespace as the separator and ignoring any quoting. So the above example is equivalent to supplying the following command / argument list.
new String[]{"/bin/sh", "-c", "'nm", "-l", "file1.o", ">", "file1.txt'"};
An alternative to pipe would be to read the stdout of your command, see Java exec() does not return expected result of pipes' connected commands for an example.
Instead of redirecting the output using "> file.txt" you would read whatever the output is and write it to a StringBuffer or OutputStream or whatever you like.
This would have the advantage that you could also read stderr and see if there were errors (like no space left on device etc.). (you can also do that using "2>" using your approach)

Categories

Resources