My experience using Java to do CLI Scripting
Stuart Marks
stuart.marks at oracle.com
Mon Apr 14 22:10:54 UTC 2025
On 4/14/25 1:48 PM, Remi Forax wrote:
>> From: "Ron Pressler" <ron.pressler at oracle.com>
>> This does what you want (and could even be combined to a single expression):
>>
>> Process p = new ProcessBuilder("ls", "-al").start();
>> String result = p.inputReader().lines().collect(Collectors.joining("\n"));
>>
>> and it’s even nicer in the cases where you may want to process the output line
>> by line, as many scripts do.
>
> Hi Ron,
> i think you need to close the inputReader
A few points here...
It might be possible to get away without any Process-related cleanup. For one,
there's no "close" method on Process or ProcessBuilder. The destroy() method will
certainly clean up, but you don't want that; and waitFor() merely waits for
termination but doesn't actually clean anything up.
At least in the Unix ProcessImpl class, there's a bunch of infrastructure to handle
exit of the underlying process, drain input from its stdout/stderr, and close the
pipes. (I didn't look on Windows.) So setting aside termination, IOException, and
what to do with stderr, it seems possible to just get the inputReader() and do
something with the characters it emits.
Getting a stream of lines with lines() seems like a reasonable thing to do if you
want to process the output line by line.
There are other possibilities, taking a nod from Files, which has methods
readAllLines() and readString(). Putting similar methods on some class in the Reader
family might help considerably here. Or you could call Reader.transferTo(Writer)
that will send the characters to any Writer that might have a useful destination.
s'marks
>
> public static String output(String... args) {
> ProcessBuilder processBuilder = new ProcessBuilder(args);
> try {
> Process process = processBuilder.start();
> try (BufferedReader reader = process.inputReader()) {
> return reader.lines().collect(Collectors.joining("\n"));
> }
> } catch (IOException e) {
> throw new IOError(e);
> }
> }
>
>
>>
>> — Ron
>>
>
> regards,
> Rémi
>
>>
>>
>>> On 14 Apr 2025, at 20:06, Cay Horstmann <cay.horstmann at gmail.com> wrote:
>>>
>>> Absolutely, ProcessBuilder/Process is the right approach.
>>>
>>> I realize that all those bells and whistles in the Process API are there for a
>>> reason, but the API is a bit clunky for the happy day path that one usually has
>>> in a script: running a process until it terminates and getting its output. It
>>> is trivial to write a couple of helper methods, but it might be nice if the
>>> Process API could help out. Something like
>>>
>>> Process p = Process.waitFor("ls", "-al");
>>> String result = p.output();
>>>
>>> Cheers,
>>>
>>> Cay
>>>
>>> PS. This isn't pretty in Python either:
>>> https://docs.python.org/3/library/subprocess.html#subprocess.run
>>>
>>>
>>> Il 12/04/25 17:02, Ron Pressler ha scritto:
>>>> Hi.
>>>> Let’s focus on ProcessBuilder and Process (as I think that’s where you want to
>>>> focus, and why I think this discussion is more appropriate for core-libs-dev).
>>>> Can you try to show more concretely what the pain point is in your actual code?
>>>> The ProcessBuilder example is long because it does multiple things, each of
>>>> which may or may not be relevant to your use case. That doing five different
>>>> things requires five lines of code doesn’t help us see where your specific pain
>>>> point is.
>>>> — Ron
>>>>> On 12 Apr 2025, at 15:39, David Alayachew <davidalayachew at gmail.com> wrote:
>>>>>
>>>>> Hello Amber Dev Team and Kulla Dev Team,
>>>>>
>>>>> (I made a reddit post too, if you prefer to interact there instead --
>>>>> https://www.reddit.com/r/java/comments/1jx87ys/)
>>>>> The following JEP's have released recently.
>>>>> • JEP 495: Simple Source Files and Instance Main Methods
>>>>> • JEP 330: Launch Single-File Source-Code Programs
>>>>> • JEP 222: jshell: The Java Shell (Read-Eval-Print Loop)
>>>>> These have made it really easy for me to do CLI scripting in Java, as opposed to
>>>>> Bash. However, I've run into some pain points, as I've relied more and more on
>>>>> Java.
>>>>> For starters, the hand off from Java --> Bash is kind of ugly. Bash --> Java is
>>>>> not bad, due to void main(final String[] args), as well as Bash's xargs. But
>>>>> Java --> Bash is ugly, and here is an example demonstrating how/why.
>>>>> I use AWS CLI to manage my dev environment. It's super powerful, and is all
>>>>> available directly from the CLI, using simple Bash or even CMD.
>>>>> Let's say I use AWS CLI to gather some ad-hoc information about my entire dev
>>>>> environment. How do I manage the multiple handoffs back and forth between AWS
>>>>> CLI and Java?
>>>>> There are no good answers.
>>>>> • Store the results into a file, then use JShell/java(c) to process the output
>>>>> file from Bash/AWS CLI.
>>>>> • There's multiple handoffs back and forth between AWS CLI and Java. So, every
>>>>> handoff from Java ---> AWS CLI means generating a new file, thus increasing the
>>>>> complexity and cruft. It's unideal.
>>>>> • Use Java's ProcessBuilder and Process classes.
>>>>> • This works, but is heavy-handed. Look at the examples in those links. That is
>>>>> multiple lines of code to represent a single bash command. It does appear to be
>>>>> the idiomatic way, though.
>>>>> • Do all upstream processing with AWS CLI in Bash directly, then do only a
>>>>> single handoff to Java, once I have done all I need to with AWS CLI.
>>>>> • This is definitely the least painful, but it also means I don't use much Java
>>>>> at all. And any changes in upstream processing must be done in Bash to avoid
>>>>> handoff headaches from AWS CLI ---> Java.
>>>>> • Download the AWS SDK Jar files and just do it all in Java.
>>>>> • Ignoring the fact that some things are much harder to do via the AWS Java
>>>>> SDK's, there's actually some functionality that just isn't available via the
>>>>> Java ones. I'd have to recreate it myself, and it would be a significant lift.
>>>>> Option 4 is best when I am building an application, but for ad-hoc checks that I
>>>>> want to do on the fly (my most common use-case by far), I have been using
>>>>> Option 3.
>>>>> I just wish I could use more Java. It's a FAR BETTERtool than Bash, but I can't
>>>>> justify the level of effort for ad-hoc use cases because of the poor hand off
>>>>> from Java --> Bash. And since AWS CLI is only available via Bash/CMD, I'm stuck
>>>>> with a bunch of not-good choices.
>>>>> CLI Scripting in Java is great, but I wanted to highlight this pain point to
>>>>> spread awareness.
>>>>> Can you relate?
>>>>>
>>>
>>> --
>>>
>>> Cay S. Horstmann | https://horstmann.com
More information about the core-libs-dev
mailing list