Printing my Mac's serial number in java using Unix commands - java

I am trying to print my mac's [edit: Apple computer] serial number in a java program. I am familiar with the Unix command
ioreg -l | awk '/IOPlatformSerialNumber/ { print $4;}'
which accomplishes this task in terminal.
When I try
String command = "ioreg -l | awk '/IOPlatformSerialNumber/ { print $4; }'"
Runtime terminal = Runtime.getRuntime();
String input = new BufferedReader(
new InputStreamReader(
terminal.exec(commands).getInputStream())).readLine();
System.out.println(new BufferedReader(
new InputStreamReader(
terminal.exec(command, args).getInputStream())).readLine());
my serial number is not printed. Instead it prints:
<+-o Root class IORegistryEntry, id 0x100000100, retain 10>
I think the problem is that terminal.exec() is not meant to take the whole command string. Is there something in java similar to the argument shell = True in python's Popen(command, stdout=PIPE, shell=True) that will allow me to pass the whole command string?

I see two possibilities:
Parse the output of ioreg -l using, say, Scanner.
Wrap the command in a shell script and exec() it:
#!/bin/sh
ioreg -l | awk '/IOPlatformSerialNumber/ { print $4;}'
Addendum: As an example of using ProcessBuilder, and incorporating a helpful suggestion by Paul Cager, here's a third alternative:
import java.io.BufferedReader;
import java.io.InputStreamReader;
public class PBTest {
public static void main(String[] args) {
ProcessBuilder pb = new ProcessBuilder("bash", "-c",
"ioreg -l | awk '/IOPlatformSerialNumber/ { print $4;}'");
pb.redirectErrorStream(true);
try {
Process p = pb.start();
String s;
// read from the process's combined stdout & stderr
BufferedReader stdout = new BufferedReader(
new InputStreamReader(p.getInputStream()));
while ((s = stdout.readLine()) != null) {
System.out.println(s);
}
System.out.println("Exit value: " + p.waitFor());
p.getInputStream().close();
p.getOutputStream().close();
p.getErrorStream().close();
} catch (Exception ex) {
ex.printStackTrace();
}
}
}

Pipes aren't supported by Runtime.exec(..) since they are a feature of shells. Instead, you'd have to emulate the pipe yourself, e.g.
String ioreg = toString(Runtime.exec("ioreg -l ").getInputStream());
Process awk = Runtime.exec("awk '/IOPlatformSerialNumber/ { print $4;}'");
write(awk.getOutputStream(), ioreg);
String input = new BufferedReader(new InputStreamReader(awk.getInputStream())).readLine();
Alternatively, you could of course run a shell as a process, e.g. Runtime.exec("bash"), and interact with it by reading and writing its IO streams. Interacting with processes is a bit tricky though and has some gotchas and let it execute your command (see comments)

To get the MAC addres via Java you can use java.net.NetworkInterface:
NetworkInterface.getByName("xxx").getHardwareAddress()
If you don't know the name (I assume it to be 'eth0' on linux) of your network interface, you can even iterate throug all of your network interfaces using NetworkInterface.getNetworkInterfaces().

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"

Excute bash script "ps aux | wc -l" in java response empty result [duplicate]

Consider the following code:
String commandf = "ls /etc | grep release";
try {
// Execute the command and wait for it to complete
Process child = Runtime.getRuntime().exec(commandf);
child.waitFor();
// Print the first 16 bytes of its output
InputStream i = child.getInputStream();
byte[] b = new byte[16];
i.read(b, 0, b.length);
System.out.println(new String(b));
} catch (IOException e) {
e.printStackTrace();
System.exit(-1);
}
The program's output is:
/etc:
adduser.co
When I run from the shell, of course, it works as expected:
poundifdef#parker:~/rabbit_test$ ls /etc | grep release
lsb-release
The internets tell me that, due to the fact that pipe behavior isn't cross-platform, the brilliant minds who work in the Java factory producing Java can't guarantee that pipes work.
How can I do this?
I am not going to do all of my parsing using Java constructs rather than grep and sed, because if I want to change the language, I'll be forced to re-write my parsing code in that language, which is totally a no-go.
How can I make Java do piping and redirection when calling shell commands?
Write a script, and execute the script instead of separate commands.
Pipe is a part of the shell, so you can also do something like this:
String[] cmd = {
"/bin/sh",
"-c",
"ls /etc | grep release"
};
Process p = Runtime.getRuntime().exec(cmd);
I ran into a similar problem in Linux, except it was "ps -ef | grep someprocess".
At least with "ls" you have a language-independent (albeit slower) Java replacement. Eg.:
File f = new File("C:\\");
String[] files = f.listFiles(new File("/home/tihamer"));
for (String file : files) {
if (file.matches(.*some.*)) { System.out.println(file); }
}
With "ps", it's a bit harder, because Java doesn't seem to have an API for it.
I've heard that Sigar might be able to help us:
https://support.hyperic.com/display/SIGAR/Home
The simplest solution, however, (as pointed out by Kaj) is to execute the piped command as a string array. Here is the full code:
try {
String line;
String[] cmd = { "/bin/sh", "-c", "ps -ef | grep export" };
Process p = Runtime.getRuntime().exec(cmd);
BufferedReader in =
new BufferedReader(new InputStreamReader(p.getInputStream()));
while ((line = in.readLine()) != null) {
System.out.println(line);
}
in.close();
} catch (Exception ex) {
ex.printStackTrace();
}
As to why the String array works with pipe, while a single string does not... it's one of the mysteries of the universe (especially if you haven't read the source code). I suspect that it's because when exec is given a single string, it parses it first (in a way that we don't like). In contrast, when exec is given a string array, it simply passes it on to the operating system without parsing it.
Actually, if we take time out of busy day and look at the source code
(at http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/lang/Runtime.java#Runtime.exec%28java.lang.String%2Cjava.lang.String[]%2Cjava.io.File%29), we find that is exactly what is happening:
public Process [More ...] exec(String command, String[] envp, File dir)
throws IOException {
if (command.length() == 0)
throw new IllegalArgumentException("Empty command");
StringTokenizer st = new StringTokenizer(command);
String[] cmdarray = new String[st.countTokens()];
for (int i = 0; st.hasMoreTokens(); i++)
cmdarray[i] = st.nextToken();
return exec(cmdarray, envp, dir);
}
Create a Runtime to run each of the process. Get the OutputStream from the first Runtime and copy it into the InputStream from the second one.
#Kaj accepted answer is for linux. This is the equivalent one for Windows:
String[] cmd = {
"cmd",
"/C",
"dir /B | findstr /R /C:"release""
};
Process p = Runtime.getRuntime().exec(cmd);

One does not simply grep into ProcessBuilder

does anyone know how to use linux grep with the java ProcessBuilder? Why does this code return an empty string when it should return "sing" ?
import java.io.*;
import java.util.*;
public class Test2 {
public static void main(String[] args) throws InterruptedException,IOException{
String line;
// Initiate grep process.
ProcessBuilder pb = new ProcessBuilder("grep", "\"sing\"", "<<<\"sing\"");
Process p = pb.start();
p.waitFor();
// Get grep output:
BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream()));
StringBuilder builder = new StringBuilder();
line = null;
while ( (line = reader.readLine()) != null) {
builder.append(line);
builder.append(System.getProperty("line.separator"));
}
String result = builder.toString();
System.out.println(result);
}
}
I also try to echo what I execute with this code:
ProcessBuilder pb = new ProcessBuilder("echo","grep", "\"sing\"", "<<<\"sing\"");
and get the correct result:
grep "sing" <<<"sing"
I finally try to execute the command at the shell and get:
sing
although it is in red font for some reason. So what am I doing wrong?
what am I doing wrong?
Something which is pretty obvious.
Do you expect, say, execve(), to understand shell constructs? No.
Well, you shouldn't be expecting ProcessBuilder to understand those either. Although it is not as low level as execve(), it is low level enough that the arguments to a command are "raw". Therefore, in your command, <<<"sing" is passed as is as an argument to grep; which means grep views it as a file to read from.
Get that in your head: what you type in the shell is interpreted by the shell; a ProcessBuilder WILL NOT use a shell to execute its processes, nor will execve(). Which, in turn, means that you cannot use shell constructs.
If you want to grep you'll have to feed your process' input with the text you want. But why use grep when Java has a builtin regex engine is another question, of course.
As to:
although it is in red font for some reason
it is simply text decoration from the grep command (well, GNU grep at least); see its manpage and the --color option. In short, in your case, it has detected that your tty had the capabilities to change the color of text and it uses that to decorate the matched text.
Try and:
echo foobar | grep foo
It will echo foobar with foo in red.
You can actually run the same command using ProcessBuilder but you have to make sure it is execute by bash. I prefer this utility method:
public static int runCmd(final String command) {
Process process=null;
int ret = 0;
String[] finalCommand = new String[] { "bash", "-c", command };
try {
final ProcessBuilder processBuilder = new ProcessBuilder(finalCommand);
processBuilder.redirectErrorStream(true);
process = processBuilder.start();
ret = process.waitFor();
// stdout+stderr
InputStreamReader isr = new InputStreamReader( process.getInputStream() );
BufferedReader br = new BufferedReader(isr);
String line;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
//System.out.println("Program terminated!");
process.destroy();
br.close();
isr.close();
}
catch (IOException|InterruptedException e) {
e.printStackTrace();
}
return ret;
}
Then call it as:
runCmd("grep -o \"sing\" <<<\"icansing\"");
And it gives me this output:
sing

Why does the below code stop with exit 1?

I tried to execute a Linux command with the below code.But it doesn't give the output just prints exit 1.When I execute the command on Linux Terminal its giving the output.When execute through Java it gives error.Please advice.
JButton btnRunningtime = new JButton("RunningTime");
btnRunningtime.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
try{
textArea.setText("");
String s2=comboBox.getSelectedItem().toString();
p = Runtime.getRuntime().exec("ps -eo pid,comm,cmd,start,etime | grep -i "+s2);
BufferedReader br = new BufferedReader(
new InputStreamReader(p.getInputStream()));
while ((s = br.readLine()) != null)
{
textArea.append(s+"\n");
System.out.println("Running time "+s);
}
p.waitFor();
System.out.println ("exit: " + p.exitValue());
p.destroy();
}
catch(Exception e)
{
System.out.println("Exception "+e);
}
}
});
btnRunningtime.setBounds(409, 211, 104, 27);
contentPane.add(btnRunningtime);
Java's Runtime.exec method does not invoke a shell to execute the command (so it is different from the standard C system call, for example). The command you're trying to pass to exec is a shell command, not a simple executable process, and you'll need a shell to get it to work.
What you need to exec is something like the array:
["sh", "-c", "ps -eo pid,comm,cmd,start,etime | grep -i "+s2]
although it's possible that you'll have to specify the first argument as "/bin/sh".
you need to read errors also from the process,
use this code
p = Runtime.getRuntime().exec("ps -eo pid,comm,cmd,start,etime | grep -i "+s2);
BufferedReader br = new BufferedReader(
new InputStreamReader(p.getErrorStream()));
the best approach is to read both, getInputStream and getErrorStream
you can start a thread for each stream to read all responses from the process.
i think you will find out whats going on.
if its not getting to this line s = br.readLine() at your current code
thats because no data is sent there,
i think data is redirected to Error stream as there is something wrong happens
Edit:
about the error, i think it's because of the parameters passed to the command
i think you need to change them into a String array
.exec(new String[]{"ps","-eo","pid,comm,...","|","grep",...});
though i am not sure on passing | as param, or some other way

How to make pipes work with Runtime.exec()?

Consider the following code:
String commandf = "ls /etc | grep release";
try {
// Execute the command and wait for it to complete
Process child = Runtime.getRuntime().exec(commandf);
child.waitFor();
// Print the first 16 bytes of its output
InputStream i = child.getInputStream();
byte[] b = new byte[16];
i.read(b, 0, b.length);
System.out.println(new String(b));
} catch (IOException e) {
e.printStackTrace();
System.exit(-1);
}
The program's output is:
/etc:
adduser.co
When I run from the shell, of course, it works as expected:
poundifdef#parker:~/rabbit_test$ ls /etc | grep release
lsb-release
The internets tell me that, due to the fact that pipe behavior isn't cross-platform, the brilliant minds who work in the Java factory producing Java can't guarantee that pipes work.
How can I do this?
I am not going to do all of my parsing using Java constructs rather than grep and sed, because if I want to change the language, I'll be forced to re-write my parsing code in that language, which is totally a no-go.
How can I make Java do piping and redirection when calling shell commands?
Write a script, and execute the script instead of separate commands.
Pipe is a part of the shell, so you can also do something like this:
String[] cmd = {
"/bin/sh",
"-c",
"ls /etc | grep release"
};
Process p = Runtime.getRuntime().exec(cmd);
I ran into a similar problem in Linux, except it was "ps -ef | grep someprocess".
At least with "ls" you have a language-independent (albeit slower) Java replacement. Eg.:
File f = new File("C:\\");
String[] files = f.listFiles(new File("/home/tihamer"));
for (String file : files) {
if (file.matches(.*some.*)) { System.out.println(file); }
}
With "ps", it's a bit harder, because Java doesn't seem to have an API for it.
I've heard that Sigar might be able to help us:
https://support.hyperic.com/display/SIGAR/Home
The simplest solution, however, (as pointed out by Kaj) is to execute the piped command as a string array. Here is the full code:
try {
String line;
String[] cmd = { "/bin/sh", "-c", "ps -ef | grep export" };
Process p = Runtime.getRuntime().exec(cmd);
BufferedReader in =
new BufferedReader(new InputStreamReader(p.getInputStream()));
while ((line = in.readLine()) != null) {
System.out.println(line);
}
in.close();
} catch (Exception ex) {
ex.printStackTrace();
}
As to why the String array works with pipe, while a single string does not... it's one of the mysteries of the universe (especially if you haven't read the source code). I suspect that it's because when exec is given a single string, it parses it first (in a way that we don't like). In contrast, when exec is given a string array, it simply passes it on to the operating system without parsing it.
Actually, if we take time out of busy day and look at the source code
(at http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/lang/Runtime.java#Runtime.exec%28java.lang.String%2Cjava.lang.String[]%2Cjava.io.File%29), we find that is exactly what is happening:
public Process [More ...] exec(String command, String[] envp, File dir)
throws IOException {
if (command.length() == 0)
throw new IllegalArgumentException("Empty command");
StringTokenizer st = new StringTokenizer(command);
String[] cmdarray = new String[st.countTokens()];
for (int i = 0; st.hasMoreTokens(); i++)
cmdarray[i] = st.nextToken();
return exec(cmdarray, envp, dir);
}
Create a Runtime to run each of the process. Get the OutputStream from the first Runtime and copy it into the InputStream from the second one.
#Kaj accepted answer is for linux. This is the equivalent one for Windows:
String[] cmd = {
"cmd",
"/C",
"dir /B | findstr /R /C:"release""
};
Process p = Runtime.getRuntime().exec(cmd);

Categories