Is there a way to get the exit code of an earlier piped Scala Process (#|)? - scala

I'm trying to pipe commands using scala's sys.process library, but I noticed that #| will return the exit code of the final process. I'm having commands earlier in the pipe fail, but the exit code of the final command is 0 as it didn't error out.
I was wondering if scala has a way to check/retrieve if previous commands in the pipe (#|) failed.
import scala.sys.process._
val p1 = ("false" #| "true").run()
assert(p1.exitValue == 1)
Bash has set -o pipefail that will pass the non-zero exit-code of a pipe, but it just seems a bit "hacky":
val p2 = Seq("/bin/bash", "-c", "set -o pipefail && false | true").run()
assert(p2.exitValue == 1)
I was hoping there might be a better way.
Thanks, I appreciate any help :)

!! operator throws an exception on non-zero exit code, so perhaps it can be used to pass output of one process to the input stream of another via #< operator only on success. If we define a custom operator #|< like so
implicit class Pipefail[T](p1: T) {
def #|<(p2: T)(implicit ev: T => ProcessBuilder): ProcessBuilder =
Try(p1.!!).map(result => (p2 #< new ByteArrayInputStream(result.getBytes))).get
}
then we could call it with
("false" #|< "true").run
which should throw
java.lang.RuntimeException: Nonzero exit value: 1
whilst
("echo Beam Me Up, Scotty" #|< "tr a-z A-Z" #|< "grep -o SCOTTY" ).run
should output uppercase SCOTTY. This will use stringToProcess implicit conversion, so remember to import scala.sys.process._

Related

Scala compilation error when running sys.process._

I am trying to run compile below function in scala
import java.io.{File, PrintWriter, StringWriter}
def runCommand(cmd:String):(Int,String)={
try {
logger.info(String.format("Trying to run the following bash command: [%s]", cmd))
import sys.process._
val intResult:Int = cmd !
val stringResult:String = cmd !!
(intResult, stringResult)
}
catch {
case e: Exception => {
logger.error(String.format("Error in running the following bash command: [%s], Program exits!", cmd))
val sw = new StringWriter
e.printStackTrace(new PrintWriter(sw))
System.out.println(sw.toString)
sys.exit(1)
}
}
(1,"1")
}
But, I am getting below error:
[ERROR] [Error] C:\Users\cp740539\IdeaProjects\sparkscala\src\main\scala\au\com\optus\bdp\conversion\PurgingPartitions.scala:213: overloaded method value !! with a
lternatives:
(log: scala.sys.process.ProcessLogger)String <and>
=> String
cannot be applied to (Int, String)
I am not sure what is the cause of the erro?
You're using the dot-less instance method arg syntax without the arg. The compiler goes looking for the arg and finds the (intResult,stringResult) tuple and tries to pass that to the !! method, which doesn't work.
Use cmd.! and cmd.!! instead.
Also: If you use a ProcessLogger then you can capture the exit code (Int), as well as StdOut and StdErr (Strings), with a single execution of cmd instead of invoking it twice, as you are doing now.

run a Ammonite scala script without typing amm

I try to use Ammonite to write scripts written in Scala
http://www.lihaoyi.com/Ammonite/#Scripting
Example from the website: Args.scala
val x = 1
import ammonite.ops._
def main(i: Int, s: String, path: Path = cwd) = {
println(s"Hello! ${s * i} ${path.relativeTo(cwd)}.")
}
to run it:
$ amm Args.scala 3 Moo
is it possible to write a script that can run by itself, instead of calling amm Args.scala, such as?
$ Args.scala 3 Moo
i tried adding the following to the head of Args.scala:
#!/usr/local/bin/amm
and making it executable with chmod +x Args.scala, but it does not work. it is as if bash (instead of amm) is running the script, and don't understand the first command val x = 1.
Try with this header #!/usr/bin/env amm.

Can a parsed inputTask be used to invoke a runTask in sbt?

I'm trying to use sbt as a general task runner (similar to rake/npm). I can get it to parse input the way I want through an inputTask, but I'm absolutely stumped how to use this to invoke a runTask/fullRunTask
val partners: List[Parser[String]] = List("foo", "bar")
val partnerParser = partners.reduce(_ | _)
val acceptArgs = (' ' ~> partnerParser ~ (' ' ~> StringBasic))
lazy val importDump = inputKey[Unit]("Import static data dump")
lazy val importDumpTask = importDump := {
val (arg1, arg2) = acceptArgs.parsed
// how can I make this call?
// ... runTask(Compile, "foo.bar.baz.DoIt.dispatch", arg1, arg2).value
}
I understand that you can't directly call tasks from other tasks, only "depend" on them so the above code won't work.
I know I can do something like
mainClass := Some("foo.bar.baz.DoIt.dispatch")
(runMain in Compile).toTask(s" foo.bar.baz.DoIt.dispatch $arg1 $arg2").value
But that means I can't use any of the parsing/autocomplete functionality.
So my question is:
How can I parse input with an inputTask, then call a main method in my code with the resulting arguments?
This is extremely painful to do in sbt. I would recommend writing a shell script (or using sbt's built-in Process support).
That said, it's possible to do this by writing a new Command that mutates the State object provided, adding the tasks you want to run as items in the remainingCommands field.

How to get status of a running Process in Scala

Earlier I runned val pb = Process("""java -version""") and it gave me an exitValue of 0 as expected but code below runs process without exiting or blocking, so how can I get exitValue, my requirement actually is how to get status of a process that runs in background without stopping.
object Sample extends App {
import scala.sys.process.Process
val pb = Process("""java -jar common-api_2.11-1.3-one-jar.jar""")
val x = pb.run
print( "Exit value :" + x.exitValue )
}
You can get all the std output of a running process by passing a ProcesLogger to the run method:
e.g.
val logger = ProcessLogger((msg: String) ⇒ println(msg))
val x = pb.run(logger)
will print all output to the System.out. -but you can pass a function that would parse and evaluate the output of the process to extract some kind of state meaningful to your application. You can also pass a different function for statndard and error output. Have a look at ProcessLogger.apply variants.

Scala interactive interpreter (REPL) - how to redirect the output to a text file?

Is it possible, and if yes how is it done? The usual > and >> that work on the Windows or Linux command line don't work in this context.
You can do it programmaticaly from console:
import java.io.FileOutputStream
import scala.Console
Console.setOut(new FileOutputStream("<output file path>"))
from now on all print and println would be directed into this file
It's unclear from your question exactly how you want to use such a thing. An example of what you are trying to do might help.
Here's an implicit function that will add a simple operator that writes any object as a String to a file. (Note that I'm using >> to mean unix-style > since > already has meaning in Scala ("less than"). You can replace this with some other operator if you like.)
implicit def anyToFileOutput(self: Any) = new {
import java.io._
def >>(filename: String) {
val f = new BufferedWriter(new FileWriter(filename))
try {
f.write(self.toString)
} finally {
if (f != null)
f.close()
}
}
}
You would use it like this:
scala> List(1,2,3) >> "out.txt"
Which produces a file, "out.txt" in the working directory containing List(1, 2, 3)
Looks to be working fine to me:
dcs#ayanami:~/github/scala (master)$ scala -e "println(2 * 2)" > output
dcs#ayanami:~/github/scala (master)$ cat output
4