linux ulimit with java does not work properly - java

I run code on linux ubuntu 17.10
public class TestExec {
public static void main(String[] args) {
try {
Process p = Runtime.getRuntime().exec(new String[]{"/bin/sh", "-c", "ulimit", "-n"});
BufferedReader in = new BufferedReader(
new InputStreamReader(p.getInputStream()));
String line = null;
while ((line = in.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
this code returns "unlimited"
but whenever I run command from terminal I get 1024.
Why those numbers are different?

You get the same result if you run the same command from the command line:
$ "/bin/sh" "-c" "ulimit" "-n"
unlimited
This is because -c only looks at the argument immediately following it, which is ulimit. The -n is not part of this argument, and is instead instead assigned as a positional parameter ($0).
To run ulimit -n, the -n needs to be part of that argument:
$ "/bin/sh" "-c" "ulimit -n"
1024
In other words, you should be using:
new String[]{"/bin/sh", "-c", "ulimit -n"}

Related

java command execution escapes '|'

I have a function to execute a system command:
public String cmd(String s) {
String out = "";
try {
Runtime run = Runtime.getRuntime();
Process pr = run.exec(s.split(" "));
pr.waitFor();
BufferedReader buf = new BufferedReader(new InputStreamReader(pr.getInputStream()));
String line = "";
while ((line=buf.readLine())!=null) {
out+=line+"\n";
}
} catch(Exception e) {
e.printStackTrace();
}
return out;
}
The command passes through:
cmd("nmap -sL -n 192.168.1.0/24 | awk '/Nmap scan report/{print $NF}'");
Expected Output:
192.168.1.0
192.168.1.1
...
Actual Output:
Starting Nmap 7.80 ( https://nmap.org ) at 2021-04-12 20:27 EET
Nmap scan report for 192.168.1.0 ...
Similar questions answers this well:
Using Java ProcessBuilder to Execute a Piped Command
Java program not getting output from terminal
To execute a pipeline, you have to invoke a shell, and then run your commands inside that shell.
Process p = new ProcessBuilder().command("bash", "-c", command).start();
bash invokes a shell to execute your command and -c means commands are read from string. So, you don't have to send the command as an array in ProcessBuilder.
Adapted to you case
String cmd(String command) {
ProcessBuilder builder = new ProcessBuilder();
builder.redirectErrorStream(true); // add stdErr to output
Process process = builder.command("bash", "-c", command).start();
StringBuilder processOutput = new StringBuilder(); // add lines easier
// try-with to auto-close resources
try (BufferedReader processOutputReader = new BufferedReader(new InputStreamReader(process.getInputStream()));) {
String readLine;
while ((readLine = processOutputReader.readLine()) != null) {
processOutput.append(readLine + System.lineSeparator()); // use system's line-break
}
process.waitFor();
}
return processOutput.toString().trim();
}
Then call as expected:
cmd("nmap -sL -n 192.168.1.0/24 | awk '/Nmap scan report/{print $NF}'");
Note: I enhanced it a bit to
use try-with-resources to deal cleanly with resources
add output from StdErr
use a StringBuilder to concatenate output lines
use System.lineSeparator for platform-independency (Win/Mac/Linux/Unix)
Inspired by:
read the output from java exec
The pipe is interpreted by the shell. It executes one command then passes the output of one command into the next one. You could emulate this in Java starting both commands and then pumping the OutputStream of the first program to the InputStream of the second.
Alternatively if you don't want to do this you can still call something like "sh -c 'command1 | command2"

Java Runtime.exec fail with space in linux

I searched a lot but did not find the solution.
My goal is using java to call commands and get output in windows and linux. I found Runtime.exec method and did some experiments.
Everything went ok except when there's space in the command parameters.
Test code as below, also in github.
The code works well on windows, but in linux, output is empty:
import java.io.BufferedReader;
import java.io.InputStreamReader;
public class Main {
public static void main(String[] args) {
try {
Runtime rt = Runtime.getRuntime();
String[] commandArray;
if (isWindows()) {
commandArray = new String[]{"cmd", "/c", "dir", "\"C:\\Program Files\""};
} else {
commandArray = new String[]{"ls", "\"/root/a directory with space\""};
}
String cmd = String.join(" ",commandArray);
System.out.println(cmd);
Process process = rt.exec(commandArray);
BufferedReader input = new BufferedReader(
new InputStreamReader(process.getInputStream()));
String result = "";
String line = null;
while ((line = input.readLine()) != null) {
result += line;
}
process.waitFor();
System.out.println(result);
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
public static boolean isWindows() {
String OS = System.getProperty("os.name").toLowerCase();
return (OS.indexOf("win") >= 0);
}
}
if I execute the printed command in bash directly, then the output is as expected.
[root#localhost javatest]# javac Main.java
[root#localhost javatest]# java Main
ls "/root/a directory with space"
[root#localhost javatest]# ls "/root/a directory with space"
a.txt b.txt
[root#localhost javatest]#
Can anyone explain why and give ways to solve?
There are two versions of exec.
exec(String command)
Here you specify a command in a similar way to how you would do it on the command-line, i.e. you need to quote arguments with spaces.
cmd /c dir "C:\Program Files"
exec(String[] cmdarray)
Here you specify the arguments separately, so the arguments are given as-is, i.e. without quotes. The exec method will take care of any spaces and quote-characters in the argument, correctly quoting and escaping the argument as needed to execute the command.
cmd
/c
dir
C:\Program Files
So, remove the extra quotes you added:
if (isWindows()) {
commandArray = new String[] { "cmd", "/c", "dir", "C:\\Program Files"};
} else {
commandArray = new String[] { "ls", "/root/a directory with space"};
}

Run Shell Commands using Java provides problems

public class RunBashCommand {
public synchronized boolean RunInBash(String command) {
System.out.println("CMD: "+command);
/*String s; not working this code also
Process p;
try {
Process p = Runtime.getRuntime().exec(command);
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());
PrintBufferReader(getError(p));
p.destroy();
} catch (Exception e) {
e.printStackTrace();
}*/
try {
Process p = new ProcessBuilder("/bin/sh", command).start();
/*Process p = new ProcessBuilder("/bin/bash", command).start();*/
PrintBufferReader(getError(p));
/*p.destroy();*/
} catch (IOException e) {
e.printStackTrace();
}
return true;
}
private static BufferedReader getOutput(Process p) {
return new BufferedReader(new InputStreamReader(p.getInputStream()));
}
private static BufferedReader getError(Process p) {
return new BufferedReader(new InputStreamReader(p.getErrorStream()));
}
private void PrintBufferReader(BufferedReader br) throws IOException {
int value = 0;
String s = "";
while((value = br.read()) != -1)
{
char c = (char)value;
s = s+c;
}
System.out.println("EEEE: "+s);
}
}
I tried this code, but it did not work.
following output came:
CMD: cd /home/jeevan/workspace/apb_proj/; source init.csh
EEEE: /bin/sh: cd /home/jeevan/workspace/apb_proj/; source init.csh: No such file or directory
CMD: cd /home/jeevan/workspace/apb_proj/verif/compile/; make clean; make compile; make elab
EEEE: /bin/sh: cd /home/jeevan/workspace/apb_proj/verif/compile/; make clean; make compile; make elab: No such file or directory
CMD: sh /home/jeevan/workspace/apb_proj/verif/test_lib/src/apb_test31/runme.csh
EEEE: /bin/sh: sh /home/jeevan/workspace/apb_proj/verif/test_lib/src/apb_test31/runme.csh: No such file or directory
can some one help?
You're effectively running:
/bin/sh "cd /home/jeevan/workspace/apb_proj/; source init.csh"
When you run /bin/sh this way, it treats its first argument as the name of a file to execute as a shell script. Of course, there's no file named "cd /home/jeevan/workspace/apb_proj/; source init.csh", so you get an error message.
The correct way to invoke sh with a command as an argument is like this:
/bin/sh -c "cd /home/jeevan/workspace/apb_proj/; source init.csh"
Using process builder, you'd do:
Process p = new ProcessBuilder("/bin/sh", "-c", command).start();
The next problem that you're likely to run into is that it appears that the command you're trying to invoke is a csh command, not an sh command. "source" is a csh command, and the file you're trying to source is called "init.csh". So maybe you want to invoke csh instead of sh:
Process p = new ProcessBuilder("/bin/csh", "-c", command).start();
You need to split command arguments into separate parameters: not ProcessBuilder("bin/sh", "cd foo/bar") but ProcessBuilder("bin/sh", "cd", "foo/bar").
You can't use shell metacharacters (like ";") too. To run multiple commands, you have to start multiple processes.
Put all your commands into a List and pass it as the argument to the ProcessBuilder. As an alternative you can start the shell process, get it's OutputStream and write commands into this stream to execute them.

How to run multiple Linux commands using java.lang.process?

I have an example like the below showed. The command
iscsiadm -m discovery -t st -p iscsiInfo.ipAddress
will be executed here, what if I want to execute extra command after executing this, like
ls /var/lib/iscsi/nodes
how to do it using java.lang.process?
Add, I only need to execute the 1st command, but I need to get the 2nd command results and show it in GUI.
public static void main(String args[]) {
try {
String line;
Process p = Runtime.getRuntime().exec("iscsiadm -m discovery -t st -p iscsiInfo.ipAddress");
BufferedReader bri = new BufferedReader
(new InputStreamReader(p.getInputStream()));
BufferedReader bre = new BufferedReader
(new InputStreamReader(p.getErrorStream()));
while ((line = bri.readLine()) != null) {
System.out.println(line);
}
bri.close();
while ((line = bre.readLine()) != null) {
System.out.println(line);
}
bre.close();
p.waitFor();
System.out.println("Done.");
}
catch (Exception err) {
err.printStackTrace();
}
}
You can run a shell which runs multiple commands.
e.g.
Runtime.getRuntime().exec("sh", "-c",
"iscsiadm -m discovery -t st -p iscsiInfo.ipAddress &&"
+" ls /var/lib/iscsi/nodes");
If you use ProcessBuilder you can redirect the error to the standard output and have one stream to read.

How to access unix shell special variables using java

How to access the unix shell special variables using java.
Few examples of unix shell special variables:
echo $$ which prints the process ID (PID) of the current shell.
echo $0 which prints the name of the command currently being executed.
echo $? which prints the exit status of the last command executed as a decimal string.
When these shell variables are included in a script file and passed the script file in ProcessBuilder argument, I'm able to execute it successfully using java. But when these variables are passed as arguments, these are not treated as variables itself. Why? How to access these special shell variables?
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
public class ExecSpecVars {
public static void main(String a[]){
InputStream is = null;
ByteArrayOutputStream baos = null;
List<String> list = new ArrayList<String>();
// String command ="/home/csk/sptest.sh";
String command ="echo $$"; //This should print the PID instead of the string "$$".
String cmd[] = command.split(" ");
for (int i = 0; i < cmd.length; i++) {
list.add(cmd[i]);
}
ProcessBuilder pb = new ProcessBuilder(list);
try {
Process prs = pb.start();
is = prs.getInputStream();
byte[] b = new byte[1024];
int size = 0;
baos = new ByteArrayOutputStream();
while((size = is.read(b)) != -1){
baos.write(b, 0, size);
}
System.out.println(new String(baos.toByteArray()));
} catch (IOException e) {
e.printStackTrace();
} finally{
try {
if(is != null) is.close();
if(baos != null) baos.close();
} catch (Exception ex){}
}
}
}
sptest.sh file contains the below commands:
echo $$
echo $?
echo $0
Run this java class to test the shell special variables in the script file:
uncomment the below line:
String command ="/home/csk/sptest.sh";
Comment the below line:
String command ="echo $$";
This is because the echo command is not resolving the $$ but bash is.
As Java does not run the command in the bash shell, this does not work.
If you run the command in bash then it will work, but it won't return what you might expect; it will return information about bash rather than the Java process:
/bin/bash -c 'echo $$' -> pid of bash process
/bin/bash -c 'echo $?' -> bash exit code
/bin/bash -c 'echo $0' -> /bin/bash
This is because you are now running another command ('/bin/bash') and the information given is about that command rather than your JVM.
In short, there is no easy way to do the things you want in Java.
Here's a quick test case to prove this (code significantly tidied and using Guava):
public static void main(final String[] args) throws Exception {
System.out.println(ManagementFactory.getRuntimeMXBean().getName());
runCommand("/bin/bash", "-c", "echo $$");
runCommand("/bin/bash", "-c", "echo $?");
runCommand("/bin/bash", "-c", "echo $0");
}
private static void runCommand(String... command) throws IOException {
ProcessBuilder pb = new ProcessBuilder(command);
Process prs = pb.start();
try (InputStream is = prs.getInputStream()) {
byte[] b = ByteStreams.toByteArray(is);
System.out.println(new String(b, StandardCharsets.UTF_8));
}
}
Output:
4466#krypto.local
4467
0
/bin/bash
So you can see that the pid is one higher than the JVM pid. And the program name is '/bin/bash'.

Categories