Even though
IPC::Run3 is the most official way, I usually prefer to write a wrapper class around
IPC::Open3, so as to provide full control over the pipes and create sufficient flexibility for having one central solution. This depends on the requirements you want to support, but, for example, you might want to fetch just some output into memory and conditionally abort. You might want to process and submit the output from one spawned process before conditionally submitting to a second one and so on. It takes some careful design to be able to cover all the possibilities that may be necessary for a site.