I am trying to get output of piped command in linux environment but so far no luck.
ProcessBuilder pb = new ProcessBuilder("/bin/sh", "-c", "top", "-b", "-n", "2", "-d", "0.2", "-p", pid + "", "|", "tail", "-1", "|", "awk", "'{print $6}'");
pb.redirectErrorStream(true);
Process p = pb.start();
p.getOutputStream().close();
try (InputStream is = p.getInputStream()) {
try (BufferedReader br = new BufferedReader(new InputStreamReader(is))) {
String line = br.readLine();
System.out.println(line);
}
}
This outputs: top: failed tty get
When I try that without specifying the script executor (/bin/bash -c): top: unknown option '|'
The shell command should be a single argument passed after -c. The invoked shell will take care of the piping and tokenization:
ProcessBuilder pb = new ProcessBuilder("/bin/sh", "-c",
"top -b -n 2 -d 0.2 -p " + pid + " | tail -1 | awk '{print $6}'");
For robustness bonus points, pass the variables as separate arguments instead of injecting them into the string (like how you'd use prepared statements in SQL):
ProcessBuilder pb = new ProcessBuilder("/bin/sh", "-c",
"top -b -n 2 -d 0.2 -p \"$1\" | tail -1 | awk '{print $6}'", "_", String.valueOf(pid));
It makes no difference when pid is an integer, but if it's an arbitrary string, this improves security and robustness.
Related
I have to join two huge files based on multiple columns. Pipe(|) symbol is the delimiter in both files.
Right now, I am generating unix join command, writing it to a shell script and then executing the shell script using ProcessBuilder to get the desired output.
The command looks something like this,
join -a 1 -a 2 -t \| -1 1 -2 1 -o 1.3,1.4,1.5,1.6,1.7,2.3,2.4,2.5,2.6,2.7 <(<"/home/input file 1" awk -F'|' 'NR>1{print $3"~&~"$4"|"$0}' | sort -k1 -t\| ) <(<"/home/input file 2" awk -F'|' 'NR>1{print $3~&~$4"|"$0}' | sort -k1 -t\| ) > "/home/output file"
This is working as expected.
I am trying to omit the step of writing the command to a shell script by using bash -c. But I am running into issues mainly because of the double quotes(") and dollar($) in the awk command. I tried to escape them using backward slash, but was of no use.
the java code I am using currently is
long pid = -1;
try {
StringBuilder completeCommand = new StringBuilder();
for(String s: commands){
completeCommand.append(s);
completeCommand.append(" ");
}
completeCommand.append(" > \"");
completeCommand.append(outputDir + File.separator + outputFileName);
completeCommand.append("\"");
File fileOutputDir = new File(outputDir);
fileOutputDir.mkdirs();
Files.writeString(Path.of(outputDir + File.separator + scriptName),
completeCommand.toString(),
new OpenOption[]{StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.WRITE,
StandardOpenOption.CREATE});
ProcessBuilder processBuilder = new ProcessBuilder("bash", "\"" + outputDir + File.separator + scriptName + "\"");
Process p = processBuilder.start();
pid = p.pid();
p.waitFor();
return pid;
} catch (IOException | InterruptedException e) {
log.error("Error in running join command" , e);
return pid;
}
When I tried to use bash -c, I just changed the ProcessBuilder statement like below,
ProcessBuilder processBuilder = new ProcessBuilder("bash", "-c", completeCommand.toString());
This doesn't throw any error, but it generates an empty file.
Is there any way I can solve this issue ?
Any help or suggestions would be great.
Thanks
I am trying to execute two linux commands using JAVA program:
ifconfig| grep -A 1 'eth0'|tail -1|cut -d ':' -f 2|cut -d ' ' -f 1
This command gives me and "IP address" and I have to read and use it in the second command
./executeTest.sh "IP address"
My function which I am using to run these commands is this:
public int exec(String[] command, Map<String, String> envt, StringBuilder stdout, StringBuilder stderr, int timeoutSeconds) throws TimeoutException, Exception{
int exitValue = -1;
final File stdoutFile = File.createTempFile("test_", "extproc.out");
final File stderrFile = File.createTempFile("test_", "extproc.err");
Process process = null;
try{
ProcessBuilder pb = new ProcessBuilder(command);
if(envt!=null){
for(Entry<String, String> entry : envt.entrySet()){
pb.environment().put(entry.getKey(), entry.getValue());
}
}
pb.redirectOutput(stdoutFile);
pb.redirectError(stderrFile);
process = pb.start();
boolean timedOut = false;
timedOut = !(process.waitFor(timeoutSeconds, TimeUnit.SECONDS));
if(timedOut){
System.out.println("Timed out waiting for process to complete.");
try{
process.destroyForcibly();
}catch(Exception killEx){
System.out.println("Error while terminating runaway process"+ killEx);
}
}else{
exitValue = process.exitValue();
}
stdout.append(FileUtils.readFileToString(stdoutFile));
stderr.append(FileUtils.readFileToString(stderrFile));
if(timedOut){
throw new TimeoutException();
}
}finally{
if(stdoutFile.exists()){
//File.deleteDirectory(stdoutFile);
}
if(stderrFile.exists()){
//FileUtils.deleteDirectory(stdoutFile);
}
if(process != null){
process.destroy();
}
}
return exitValue;
}
However, I am getting the following error when I call this function for both the commands written above:
java.io.IOException: Cannot run program "ifconfig | grep -A 1 'eth0' | tail -1 |cut -d ':' -f 2 |cut -d ' ' -f 1": error=2, No such file or directory
at java.lang.ProcessBuilder.start(ProcessBuilder.java:1048)
at IOxUnifiedSanityTestSuite.starWebServer.exec(starWebServer.java:66)
at IOxUnifiedSanityTestSuite.starWebServer$2.handle(starWebServer.java:148)
at IOxUnifiedSanityTestSuite.starWebServer$2.handle(starWebServer.java:124)
at io.vertx.ext.web.impl.RouteImpl.handleContext(RouteImpl.java:217)
at io.vertx.ext.web.impl.RoutingContextImplBase.iterateNext(RoutingContextImplBase.java:78)
at io.vertx.ext.web.impl.RoutingContextImpl.next(RoutingContextImpl.java:133)
at io.vertx.ext.web.impl.RouterImpl.accept(RouterImpl.java:79)
at io.vertx.core.http.impl.ServerConnection.handleRequest(ServerConnection.java:288)
at io.vertx.core.http.impl.ServerConnection.processMessage(ServerConnection.java:421)
at io.vertx.core.http.impl.ServerConnection.handleMessage(ServerConnection.java:134)
at io.vertx.core.http.impl.HttpServerImpl$ServerHandler.doMessageReceived(HttpServerImpl.java:623)
at io.vertx.core.http.impl.HttpServerImpl$ServerHandler.doMessageReceived(HttpServerImpl.java:573)
at io.vertx.core.http.impl.VertxHttpHandler.lambda$channelRead$0(VertxHttpHandler.java:71)
at io.vertx.core.impl.ContextImpl.lambda$wrapTask$2(ContextImpl.java:322)
at io.vertx.core.impl.ContextImpl.executeFromIO(ContextImpl.java:190)
at io.vertx.core.http.impl.VertxHttpHandler.channelRead(VertxHttpHandler.java:71)
at io.vertx.core.net.impl.VertxHandler.channelRead(VertxHandler.java:122)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:363)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:349)
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:341)
at io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:293)
at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:267)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:363)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:349)
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:341)
at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1334)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:363)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:349)
at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:926)
at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:129)
at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:642)
at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:565)
at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:479)
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:441)
at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:858)
at java.lang.Thread.run(Thread.java:745)
Caused by: java.io.IOException: error=2, No such file or directory
at java.lang.UNIXProcess.forkAndExec(Native Method)
at java.lang.UNIXProcess.<init>(UNIXProcess.java:248)
at java.lang.ProcessImpl.start(ProcessImpl.java:134)
at java.lang.ProcessBuilder.start(ProcessBuilder.java:1029)
... 36 more
The way I am calling exec function is this:
String command1[] = new String[]{"ifconfig | grep -A 1 \'eth0\' | tail -1 |cut -d \':\' -f 2 |cut -d \' \' -f 1"};
StringBuilder stdout = new StringBuilder();
StringBuilder stderr = new StringBuilder();
exec(command1, null, stdout, stderr, 30)
String command2[] = new String[]{"./executeTest.sh ipaddress"};
StringBuilder stdout1 = new StringBuilder();
StringBuilder stderr1 = new StringBuilder();
exec(command2, null, stdout1, stderr1, 30)
Can anyone help me in finding out what I am doing wrong here?
You probably feed your first command as a whole to ProcessBuilder's constructor:
"ifconfig| grep -A 1 'eth0'|tail -1|cut -d ':' -f 2|cut -d ' ' -f 1"
ProcessBuilder considers it to be a single program name, hence the error.
Try passing it the following:
new String{"/bin/bash", "-c", "ifconfig| grep -A 1 'eth0'|tail -1|cut -d ':' -f 2|cut -d ' ' -f 1"}
like
exec(new String{"/bin/bash", "-c", "ifconfig| grep -A 1 'eth0'|tail -1|cut -d ':' -f 2|cut -d ' ' -f 1"},
envt, stdout, stderr, timeoutSeconds);
ProcessBuilder will invoke bash, which in turn will invoke the complex command.
I had tested the following methods to execute Linux command from my program
Method 1 : Assign all into a string
String temp1 = "'/"+t2+"/,/"+t1+"/p'";
String command2 = "sed -n "+temp1+" app.log";
Process p1 = Runtime.getRuntime().exec(command2);
Method 2 : use array
String [] command2 = new String []{"sed","-n","'/",t2,"/,/",t1,"/p'", "app.log";
System.out.println("The command2 is : "+Arrays.toString(command2);
Process p2 = new ProcessBuilder(command2).start();
This my reference link for the method 2 but both of the methods not working at all. This is the command I hope to run in the terminal sed -n '/14:32:54/,/14:33:44/p' app.log
This is a portion of my code for calling the system command, nothing displayed in line2 variable
String [] command2 = new String []{"sed","-n","'/",t2,"/,/",t1,"/p'","stlog.txt"};
Process p2 = new ProcessBuilder(command2).start();
BufferedReader br2 = new BufferedReader(new InputStreamReader(p2.getInputStream()));
String line2;
while((line2 = br2.readLine()) != null)
{
System.out.println(line2);
}
In my case worked:
ProcessBuilder processBuilder = new ProcessBuilder("/bin/bash", "-c", "pwd")
.directory(new File("some.directory.path"));
Process process = processBuilder.start();
Or you can sip using ProcessBuilder and just call
String command = "ping www.google.com";
String[] commandArray = {"/bin/bash", "-c", "pwd"};
Runtime.getRuntime().exec(commandArray);
"/bin/bash" 0 means that you are going to exec command in bach
"-c" -defines that next param is command
command - any command like "ping www.google.com" or "./script.sh" that you execute with terminal
you should just place your command instead of "ping www.google.com", but as you haven't specified directory - script will be executed from project directory (you can check it by executing "pwd" command that prints current directory). That is why ProcessBuilder is more preferable, as you can indicate execution directory there (replace "some.directory.path" with your dir).
.directory(new File("path/to/some/dir"));
I'm running a shell script command in java program using ProcessBuilder, here is my code :
String lastLine = "";
ProcessBuilder processBuilder = new ProcessBuilder("/bin/bash", "-c", "echo $(ps -eo pid,args | grep -v grep | grep -v \"$$"\ | grep feature_service.sh | awk '{print $1}')");
BufferedReader reader =
new BufferedReader(new InputStreamReader(process.getInputStream()));
String line;
while ((line = reader.readLine()) != null) {
lastLine = line;
}
Output from this : empty string ("")
But if i run the same command on terminal it is working fine (pid of process).
Please help me.
Following simplified Shell command is working.
ProcessBuilder processBuilder = new ProcessBuilder("/bin/bash",
"-c",
"ps -eo pid,args|grep [f]eature_service.sh|awk '{print $1}'|tr '\\n' ' '");
ps -eo pid,args - list the process ID and the arguments
grep [f]eature_service.sh - grep for the string feature_service.sh in the arguments, the [f] avoid the multiple usage of grep in the chain
awk '{print $1}' - print the first column of the output, using default whitespace characters as delimiter
tr '\n' ' ' - replace all newline cracaters in the output by a space character
I'm executing some commands from the command line in my java program, and it appears that it doesn't allow me to use "grep"? I've tested this by removing the "grep" portion and the command runs just fine!
My code that DOESN'T work:
String serviceL = "someService";
Runtime rt = Runtime.getRuntime();
Process proc = rt.exec("chkconfig --list | grep " + serviceL);
Code that does work:
Runtime rt = Runtime.getRuntime();
Process proc = rt.exec("chkconfig --list");
Why is this? And is there some sort of correct method or workaround? I'm aware that I could just parse the entire output, but I would find it easier to do it all from the command line. Thanks.
The pipe (like redirection, or >) is a function of the shell, and so executing it directly from Java won't work. You need to do something like:
/bin/sh -c "your | piped | commands | here"
which executes a shell process within the command line (including pipes) specified after the -c (in quotes).
So, here's is a sample code that works on my Linux OS.
public static void main(String[] args) throws IOException {
Runtime rt = Runtime.getRuntime();
String[] cmd = { "/bin/sh", "-c", "ps aux | grep skype" };
Process proc = rt.exec(cmd);
BufferedReader is = new BufferedReader(new InputStreamReader(proc.getInputStream()));
String line;
while ((line = is.readLine()) != null) {
System.out.println(line);
}
}
Here, I'm extracting all my 'Skype' processes and print the content of the process input stream.
You're trying to use piping which is a function of the shell ... and you're not using a shell; you're exec'ing the chkconfig process directly.
The easy solution would be to exec the shell and have it do everything:
Process proc = rt.exec("/bin/sh -c chkconfig --list | grep " + serviceL);
That being said ... why are you piping to grep? Just read the output of chkconfig and do the matching yourself in java.
String[] commands = { "bash", "-c", "chkconfig --list | grep " + serviceL };
Process p = Runtime.getRuntime().exec(commands);
or if you are in a linux env just use grep4j