I'm executing a command in order to write log messages into a file on the sdcard. This is already working:
String command = "logcat -f /storage/sdcard1/test.log";
Process logcatProcess = Runtime.getRuntime().exec(command);
Now I want to filter the log output by grep before writing it to the file like this:
String[] command = {
"/system/bin/sh",
"-c",
"logcat | grep -i Test01 >> /storage/sdcard1/test.log"
};
Process logcatProcess = Runtime.getRuntime().exec(command);
When I run the code from above the file is created but with no content. The grep command is surely installed on the system. What is wrong here?
logcat is outputting to grep live, but grep is processing data in big batches. Eventually, if there's enough output, grep should write something out.
(If you're writing to the terminal instead of to a file, grep is slightly less efficient and flushes after every line it sees, making it feel more interactive. This is not specific to Android's grep; you'll see this behavior on Linux or OS X also.)
If you use logcat -d, it'll terminate instead of polling for more logs, and grep will write out and terminate too. If that's not the behavior you want, you may end up needing to use ProcessBuilder to wrap logcat and re-implement grep (without buffering) inside your Java application.
Related
I'm having the next error when execute a cmd command using Java. I'm working in a mac laptop. This is my code:
private static String exportContainerFromImage(String container) {
//docker export mysql_dummy > ~/Documents/mysql_dummy.tar
String errorMessage = "";
String[] cmdArgs =
{"docker export mysql_dummy > ~/Documents/mysql_dummy.tar", "bash"};
Process process = Runtime.getRuntime().exec(cmdArgs);
}
But I'm getting the error, error=2, No such file or directory, if I execute the command directly on the terminal it runs successfully, I tried also changing the directory to ~\\Documents\\mysql_dummy.tar and got the same result.
But if I run the command with the arguments:
{"docker create -ti --name mysql_dummy mysql", "bash"};
It runs properly
Any ideas?
You're conflating 'shell magic' with 'an OS'. Also, you seem to be wildly confused about what the array form of cmdArgs does, because you've tagged a bash in there at the end. That array is supposed to contain the executable's full path at arr[0], and all arguments at arr[1] and up. docker create -ti ... is clearly not a filename, and bash is clearly not an argument.
Shell magic?
If you type:
docker create -ti --name mysql_dummy mysql
on the command line, bash (or cmd.exe if on windows, or whatever shell you are using) reads it and does a whole bunch of replacement magic and parsing on this. It's the shell that does this, not the OS, and java's processbuilder stuff is not a shell and therefore isn't going to do all that. What you're attempting to do? Run that entire line as if it's a single file name that is executable which it clearly isn't.
This is all shell magic - all things that you CANNOT do with exec. Fortunately, java is a programming language, so you can do all these things by, well, programming it.
Parsing out params by splitting on whitespace.
quoting to avoid that splitting, but then removing the quotes.
Treating ~ as a ref to a homedir.
Replacing * and ? in filename paths.
Variable substitution
Setting up redirects with > somefile.txt or 2> /dev/null or < file.in or whatnot.
You must do those things.
In addition, exec cannot be used to this, period. As usual, the only non-problematic way to run processes is to always use ProcessBuilder, no exceptions. Consider runtime.exec a known-broken method you must never call.
ProcessBuilder lets you redirect the output.
String[] cmdArgs = {
"/bin/docker" // note, FULL PATH!
"export",
"mysql_dummy"
};
File out = new File(System.getProperty("user.home"), "Documents/mysql_dummy.tar");
ProcessBuilder pb = new ProcessBuilder(cmdArgs);
pb.redirectOutput(new File(out));
pb.start();
That does what you want, presumably.
The alternative is to make a script (script.sh or script.bat) and then start bash or cmd.exe and ask it to run that script.
String[] args = { "/bin/bash", "-c", "/fully/qualified/path/to/the/script.sh" }
and then exec that. Now you can pile *.txt, > foobar.txt, ~/homediref, and all the other shellisms in that script as much as you like.
Overall #rzwiterloot's answer is good, but there are some alternatives it leaves out.
First, what I would consider the best solution to this problem: the -o option to docker export.
"docker", "export", "mysql_dummy", "-o", "Documents/mysql_dummy.tar"
Ignoring ~/ here, this set of command and arguments will achieve the same thing as ... > Documents/mysql_dummy.tar but doesn't rely on the shell for the redirection; docker export is perfectly capable of handling that operation itself.
Second, if you wanted to run a shell command from the program, you could. I would not recommend this. But in certain circumstances it might make sense.
The alternative is to make a script
You don't have to put the command in a separate file. Actually this is one inaccuracy in #rzwiterloot's answer; -c allows you to pass command(s) to bash, not the path to a file containing commands.
"bash", "-c", "docker export mysql_dummy > ~/Documents/mysql_dummy.tar"
However, I'd recommend you avoid invoking shells from any program you write. They're quirky and esoteric and there's almost always a simpler way to achieve what you want, such as docker export's -o optiopn, in this case.
The goal of my program is to run an interactive command line executable from Java, so I can add input partway through when required. Basically redirecting input.
I couldn't find anything that worked online because the -c flag does not allow interactivity, but then I saw that the -i flag in the terminal allowed me to run commands with interactive input if I fed it a .sh file.
However, when I tried using this flag in java, it didn't work. I have separate input and output threads, so if I could get this to work it seems like it would be easy.
Relevant code:
ProcessBuilder pb = new ProcessBuilder()
.directory(new File(testDir))
.inheritIO()
.command("bash", "-i"
,"executor.sh");
proc = pb.start();
this is the error i get:
bash: cannot set terminal process group (1469): Inappropriate ioctl for device
bash: no job control in this shell
If there's way I could get this -i option working, then I'd appreciate pointers to something else that would allow me to get interactive input working because nothing else that I've tried seems to solve this problem.
bash -i is completely unrelated to ability to read from the TTY.
Rather, redirect from the TTY, after your script already started:
#!/usr/bin/env bash
exec </dev/tty || { echo "ERROR: Unable to connect stdin to /dev/tty" >&2; exit 1; }
read -r -p "Fill out this prompt please: " value
echo "Read from TTY: $value"
The command exec </dev/tty replaces the script's stdin (FD 0) with a read handle on /dev/tty. If you wanted to do this just for a single command, rather than for the whole script, put </dev/tty on the end of that command.
Of course, this only works if your process is run in a context where it has a controlling terminal at all -- but if that weren't the case, you couldn't read from the user without getting some kind of handle on an I/O device regardless.
I'm trying to use the "top" command from a standalone java app. I basically want to run this:
top -p myProcessId -n 1 -b
my java psuedocode:
Process process = Runtime.getRuntime().exec("top -p myProcessId -n 1 -b");
// read both:
process.getInputStream();
process.getErrorStream()
and the output I get is:
top: failed tty get
I'm not sure what that output means - any ideas how to use "top" correctly here? I'm on ubuntu fwiw.
Thanks
----- Update -------------
OK I was able to do this instead with:
ps -Ao pid,rsz,cmd
and then I read through each output line for the process I'm interested in, I can read the res mem value from there.
top doesn't use the ordinary standard output stream; it manipulates the terminal device directly. This means that you can't just read in its output like you can from most command-line programs. There are a few other programs that might do what you need (ps is a likely candidate), or you might be able to read the relevant information from /proc.
I am trying to accomplish two things:
I am running cygwin on Windows7 to execute my unix shell commands and I need to automate the process by writing a Java app. I already know how to use the windows shell through Java using the 'Process class' and Runtime.getRuntime().exec("cmd /c dir"). I need to be able to do the same with unix commands: i.e.: ls -la and so forth. What should I look into?
Is there a way to remember a shell's state?
explanation: when I use: Runtime.getRuntime().exec("cmd /c dir"), I always get a listing of my home directory. If I do Runtime.getRuntime().exec("cmd /c cd <some-folder>") and then do Runtime.getRuntime().exec("cmd /c dir") again, I will still get the listing of my home folder. Is there a way to tell the process to remember its state, like a regular shell would?
It seems that the bash command line proposed by PaĆlo does not work:
C:\cygwin\bin>bash -c ls -la
-la: ls: command not found
I am having trouble figuring out the technicalities.
This is my code:
p = Runtime.getRuntime().exec("C:\\cygwin\\bin\\bash.exe -c ls -la");
reader2 = new BufferedReader(new InputStreamReader(p.getInputStream()));
line = reader2.readLine();
line ends up having a null value.
I added this to my .bash_profile:
#BASH
export BASH_HOME=/cygdrive/c/cygwin
export PATH=$BASH_HOME/bin:$PATH
I added the following as well:
System Properties -> advanced -> Environment variables -> user variebales -> variable: BASH, value: c:\cygwin\bin
Still nothing...
However, if I execute this instead, it works!
p = Runtime.getRuntime().exec("c:\\cygwin\\bin\\ls -la ~/\"Eclipse_Workspace/RenameScript/files copy\"");
1. Calling unix commands:
You simply need to call your unix shell (e.g. the bash delivered with cygwin) instead of cmd.
bash -c "ls -la"
should do. Of course, if your command is an external program, you could simply call it directly:
ls -la
When starting this from Java, it is best to use the variant which takes a string array, as then
you don't have Java let it parse to see where the arguments start and stop:
Process p =
Runtime.getRuntime().exec(new String[]{"C:\\cygwin\\bin\\bash.exe",
"-c", "ls -la"},
new String[]{"PATH=/cygdrive/c/cygwin/bin"});
The error message in your example (ls: command not found) seems to show that your bash can't find the ls command. Maybe you need to put it into the PATH variable (see above for a way to do this from Java).
Maybe instead of /cygdrive/c/cygwin/bin, the right directory name would be /usr/bin.
(Everything is a bit complicated here by having to bridge between Unix and Windows
conventions everywhere.)
The simple ls command can be called like this:
Process p = Runtime.getRuntime().exec(new String[]{"C:\\cygwin\\bin\\ls.exe", "-la"});
2. Invoking multiple commands:
There are basically two ways of invoking multiple commands in one shell:
passing them all at once to the shell; or
passing them interactively to the shell.
For the first way, simply give multiple commands as argument to the -c option, separated by ; or \n (a newline), like this:
bash -c "cd /bin/ ; ls -la"
or from Java (adapting the example above):
Process p =
Runtime.getRuntime().exec(new String[]{"C:\\cygwin\\bin\\bash.exe",
"-c", "cd /bin/; ls -la"},
new String[]{"PATH=/cygdrive/c/cygwin/bin"});
Here the shell will parse the command line as, and execute it as a script. If it contains multiple commands, they will all be executed, if the shell does not somehow exit before for some reason (like an exit command). (I'm not sure if the Windows cmd does work in a similar way. Please test and report.)
Instead of passing the bash (or cmd or whatever shell you are using) the commands on the
command line, you can pass them via the Process' input stream.
A shell started in "input mode" (e.g. one which got neither the -c option nor a shell script file argument) will read input from the stream, and interpret the first line as a command (or several ones).
Then it will execute this command. The command itself might read more input from the stream, if it wants.
Then the shell will read the next line, interpret it as a command, and execute.
(In some cases the shell has to read more than one line, for example for long strings or composed commands like if or loops.)
This will go on until either the end of the stream (e.g. stream.close() at your side) or executing an explicit exit command (or some other reasons to exit).
Here would be an example for this:
Process p = Runtime.getRuntime().exec(new String[]{"C:\\cygwin\\bin\\bash.exe", "-s"});
InputStream outStream = p.getInputStream(); // normal output of the shell
InputStream errStream = p.getInputStream(); // error output of the shell
// TODO: start separate threads to read these streams
PrintStream ps = new PrintStream(p.getOutputStream());
ps.println("cd /bin/");
ps.println("ls -la");
ps.println("exit");
ps.close();
You do not need cygwin here. There are several pure Java libraries implementing SSH protocol. Use them. BTW they will solve your second problem. You will open session and execute command withing the same session, so the shell state will be preserved automatically.
One example would be JSch.
I need some help writing a command that will be put into a .sh. I want to return the process id, which in the output below is 3678, but I'm having diffuclty because the process id changes everytime it gets restarted, so my code breaks
Output:
[root#server1 /usr/home/aaron]# ps -xauww | grep java | grep www
www 3678 0.0 3.2 1308176 267864 ?? Is 3:21PM 0:17.19 [java]
[root#server1 /usr/home/aaron]#
Heres what I was doing until I realized the column changed when the pid changed:
ps -xauww | grep java | grep www | cut -d" " -f6
Any help is appreciated. thanks.
If the starting is automated by a shell script, you can write the pid of the just-started-process which is in the variable $!.
java ...... &
echo "$!" > myjavaprogram.pid
When you need to kill it, just do:
kill `cat myjavaprogram.pid`
Below pgrep command works for getting pid by jar-file name:
pgrep -f test-app.jar
As per http://cfajohnson.com/shell/cus-faq-2.html
How do I get a process id given a process name? Or, how do I find
out if a process is still running, given a process ID?
There isn't a reliable way to to this portably in the shell. Some
systems reuse process ids much like file descriptors. That is,
they use the lowest numbered pid which is not currently in use
when starting a new process. That means that the pid you're
looking for is there, but might not refer to the process you think
it does.
The usual approach is to parse the output of ps, but that involves
a race condition, since the pid you find that way may not refer to
the same process when you actually do something with that
pid. There's no good way around that in a shell script though, so
be advised that you might be stepping into a trap.
One suggestion is to use pgrep if on Solaris, and 'ps h -o pid -C
$STRING' if not, and your ps supports that syntax, but neither of
those are perfect or ubiquitous.
The normal solution when writing C programs is to create a pid
file, and then lock it with fcntl(2). Then, if another program
wants to know if that program is really running, it can attempt to
gain a lock on the file. If the lock attempt fails, then it knows
the file is still running.
We don't have options in the shell like that, unless we can supply
a C program which can try the lock for the script. Even so, the
race condition described above still exists.
That being said look at this: http://www.faqs.org/faqs/unix-faq/faq/part3/section-10.html it might help you out ?
One way can be found in: man pgrep