Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DSL for using a String as the input of a process #11668

Open
jroper opened this issue Aug 8, 2019 · 5 comments

Comments

@jroper
Copy link

commented Aug 8, 2019

Here's my exact use case, I want to write the Scala version of this shell script:

docker build -t graalvm-native-image - <<DOCKERFILE
FROM oracle/graalvm-ce:19.0.0
WORKDIR /opt/graalvm
RUN gu install native-image
ENTRYPOINT [native-image]
DOCKERFILE

Naively I thought this might work:

val dockerfile = """FROM oracle/graalvm-ce:19.0.0
WORKDIR /opt/graalvm
RUN gu install native-image
ENTRYPOINT [native-image]
"""

("docker build -t graalvm-native-image:19.0.0 -" #< dockerfile).!

But of course it doesn't work because String is executed as a command, not used as the source of input, so the error you get is Cannot run program "FROM". This works:

("docker build -t graalvm-native-image:19.0.0 -" #< 
  new java.io.ByteArrayInputStream(dockerfile.getBytes("utf-8"))).!

But is not very nice from a DSL perspective. There should be a straight forward way to pass a String as the input of a command. Here are some examples of potential APIs that could work:

(Process.echo(dockerfile) #> "docker build -t graalvm-native-image:19.0.0 -").!

("docker build -t graalvm-native-image:19.0.0 -" #<<< dockerfile).!

Similar APIs could be supplied for byte arrays.

@SethTisue

This comment has been minimized.

Copy link
Member

commented Aug 8, 2019

does https://lihaoyi.github.io/Ammonite/#Ammonite-Shell offer anything here?

scala.sys.process is pretty neglected these days

@jroper

This comment has been minimized.

Copy link
Author

commented Aug 8, 2019

Probably, but I don't really want a shell. scala.sys.process is used heavily by sbt and its plugins, along with in an ad-hoc manner by sbt users in their build files.

@ritschwumm

This comment has been minimized.

Copy link

commented Aug 8, 2019

how about sth like this:

implicit final class StringContextExt(peer:StringContext) {
    def stream(args:Any*):InputStream = new java.io.ByteArrayInputStream(peer.s(args:_*).getBytes("UTF-8"))
}
@som-snytt

This comment has been minimized.

Copy link

commented Aug 8, 2019

@SethTisue You mean PRs about scala.sys.process are neglected. scala/scala#8269 It would be less neglected as the promised module.

The current unDSL (and somehow I thought it would be easier):

scala> import scala.sys.process._
import scala.sys.process._

scala> def text: java.io.OutputStream => Unit = { out => out.write("hello, world".getBytes) ; out.close() }
text: java.io.OutputStream => Unit

scala> "head -10" run BasicIO.standard(text)  // doesn't use ThreadProcess and PipedProcess
hello, worldres0: scala.sys.process.Process = scala.sys.process.ProcessImpl$SimpleProcess@47044f7d

scala> "head -10".#<<("mytext")  // would be nice, #<< is currently for redirect to file
                  ^
       error: value #<< is not a member of String

scala> new java.io.File("foo.out") #<< new java.io.ByteArrayInputStream("hello".getBytes)
res11: scala.sys.process.ProcessBuilder =  ( <input stream> #| /home/amarki/snips/foo.out )

scala> .!
res12: Int = 0

scala> "head -10" ! BasicIO.standard(text)  // should already work, it's defined for ProcessBuilderImpl but not ProcessBuilder
                                    ^
       error: type mismatch;
        found   : scala.sys.process.ProcessIO
        required: scala.sys.process.ProcessLogger

I like echo, but I also like the idea of leveraging "just read the string without more threads".

The current DSL has ! for exit value, !! for stdout.toString. Optionally add < to mean read stdin. Optionally provide a ProcessLogger to process stdout/stderr.

So one proposal would be "add << to mean read from string". It would be slightly awkward with a logger: "head -10" !!<< ("some long input", ProcessLogger(err => println(err))).

I guess it kind of works:

scala> import scala.sys.process._
import scala.sys.process._

scala> "head -10" !<< "text"
textres0: Int = 0

scala> "head -10" !!<< "text"
res1: String =
"text
"

scala> "head -10" !!<< ("text", ProcessLogger(err => println(s"darn, $err")))
res2: String =
"text
"

scala> "head -10" !!<< """Now is the time
     | for all do-gooders
     | to do Some(good)."""
res3: String =
"Now is the time
for all do-gooders
to do Some(good).
"

scala> "head -10" #> new java.io.File("junk.out") !<< "hello, world"
res4: Int = 0

@som-snytt

This comment has been minimized.

Copy link

commented Aug 8, 2019

Or same as echo, first-class process:

scala> "head -2" #<< "hello"
res0: scala.sys.process.ProcessBuilder =  ( <here hello> #| [head, -2] )

scala> .!!
res1: String =
"hello
"

scala> "head -2" #<< """goodbye
     | cruel
     | world"""
res2: scala.sys.process.ProcessBuilder =  ( <here goodb...> #| [head, -2] )

scala> .!!
res3: String =
"goodbye
cruel
"

To go full-on Mark Harrah.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
4 participants
You can’t perform that action at this time.