----- Original Message ----- > From: "Stuart Marks" <stuart.ma...@oracle.com> > To: "Remi Forax" <fo...@univ-mlv.fr>, "Ron Pressler" > <ron.press...@oracle.com>, "David Alayachew" > <davidalayac...@gmail.com> > Cc: "cay horstmann" <cay.horstm...@gmail.com>, "core-libs-dev" > <core-libs-dev@openjdk.org> > Sent: Tuesday, April 15, 2025 12:10:54 AM > Subject: Re: My experience using Java to do CLI Scripting
> On 4/14/25 1:48 PM, Remi Forax wrote: >>> From: "Ron Pressler" <ron.press...@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. You mean by making the Process Closeable ? > > Getting a stream of lines with lines() seems like a reasonable thing to do if > you > want to process the output line by line. with the caveat that you have to close() the returned stream (like Files.lines()), something people will often forget. > > 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. +1 for readAllInputLines() and readInputString() on Process. > > s'marks regards, Rémi > > > > > >> >> 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.horstm...@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 <davidalayac...@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