How to exec subprocess which exists non-zero

UNIX grep exits non-zero if it didn’t find any matches.
Unfortunately sys.process.!! throws an exception when this happens.

What is the correct way to exec a subprocess without treating non-zero exit status as an exception.

In general I might still want the stdout of such a process which exited non-zero. In the case of grep there was no stdout by definition, but in general a process has stderr, stdout, and exit status.

I see there is a version of !! which takes a ProcessLogger to handle the stdout and stderr, but it does not handle the exit status. It is still documented to throw an annoying exception of the status is non-zero.

1 Like

Just implement what you want on top?

import sys.process._

case class ProcessCapture(exit: Int, out: Vector[String])

class CaptProcessLogger extends ProcessLogger {
  private var _output: Vector[String] = Vector.empty
  override def out(s: => String): Unit = _output :+= s
  override def err(s: => String): Unit = Console.err.println(s)
  override def buffer[T](f: => T): T = f
  def output: Vector[String] = _output
}

implicit class CaptProcessBuilder(val strSeq: Seq[String]) extends AnyVal {
  def !!! : ProcessCapture = {
    val log = new CaptProcessLogger
    val exit = strSeq.!(log)
    ProcessCapture(exit, log.output)
  }
}

Test ride:

Seq("grep", "ba", "/home/patrick/test.txt").!!! // ProcessCapture(0,Vector(bar, baz))
Seq("grep", "x", "/home/patrick/test.txt").!!! // ProcessCapture(1,Vector())

Again, I’d probably check whether Ammonite feels more convenient than raw sys.process. Ammonite % and %% ops behave pretty much like sys.process ! and !!, but %% already has a notion of CommandResult.

import ammonite.ops._

val %%% = {
  def executeSafe(wd: Path, cmd: Command[_]): CommandResult =
    try {
      Shellout.executeStream(wd, cmd)
    }
    catch {
      case exc: ShelloutException => exc.result
    }
  Command(Vector.empty, Map.empty, executeSafe)
}

Test ride:

import os.root

val wd = root / "home" / "patrick"
(%%%grep("ba", "test.txt"))(wd) // CommandResult(0, [bar, baz])
(%%%grep("x", "test.txt"))(wd) // CommandResult(1, [])
1 Like