首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何用最少额外样板在Scala中为命令行创建DSL

如何用最少额外样板在Scala中为命令行创建DSL
EN

Stack Overflow用户
提问于 2012-05-22 18:46:25
回答 6查看 1.1K关注 0票数 8

我需要为不熟悉scala (也不是Java)但熟悉Shell的用户开发API。基本上,他们会在scala类中编写shell脚本(我知道我只需要调用外部shell脚本,但是来吧!另外,稍后我们将为常见的shell任务提供一些函数)。

我希望能实现这样的目标:

代码语言:javascript
复制
1 object MyCoolScript extends MyMagicTrait {
2   $ "mkdir /tmp/test"
3   $ "cd /tmp/test"
4   $ "wget some-url"   
5 }

更直接地说,我如何将第2-4行(或可能不那么简洁的版本)转换为可以在SeqString中处理的MyMagicTrait?

我知道sys.process.stringToProcess的事,但如果我知道:

代码语言:javascript
复制
object MyCoolScript extends MyMagicTrait {
  "mkdir /tmp/test" !!
  "cd /tmp/test" !!
  "wget some-url" !!  
}

如何以简洁的方式获得每个命令的结果?另外,我还希望得到一个$ "xxx“符号。

发布最新答案:

多亏了“debilski”、“tenshi”和“daniel-c-sobral”,我才能得到一个非常接近所需实现的实现:https://gist.github.com/2777994

EN

回答 6

Stack Overflow用户

回答已采纳

发布于 2012-05-22 20:50:31

似乎Scala2.10附带的字符串内插可以在这里帮助您。首先,您可以实现简单的$方法,该方法只需立即执行命令。为了使其生效,您需要在StringContext上添加此自定义方法

代码语言:javascript
复制
object ShellSupport {
    implicit class ShellStrings(sc: StringContext) {
        def $(args: Any*) = 
            sc.s(args: _*) split "\n" map (_.trim) filterNot (_.isEmpty) foreach { cmd =>
                // your excution logic goes here
                println(s"Executing: $cmd")
            }

    }
} 

现在您可以这样使用它:

代码语言:javascript
复制
import ShellSupport._

val testDir = "/tmp/test"

$"mkdir $testDir"
$"cd $testDir" 
$"""
    wget some-url
    wget another-url
 """ 

您可以利用它的语法(它唯一的缺点是不能在$"之间添加空间)和命令中的字符串内插。

现在让我们试着实现你的神奇特质。这通常是相同的想法,但我也使用DelayedInit来正确定义命令,然后在创建类时自动执行它们。

代码语言:javascript
复制
trait MyMagicTrait extends DelayedInit {
    private var cmds: List[String] = Nil

    def commands = cmds

    implicit class ShellStrings(sc: StringContext) {
        def $(args: Any*) = {
            val newCmds = sc.s(args: _*) split "\n" map (_.trim) filterNot (_.isEmpty)
            cmds = cmds ++ newCmds
        }
    }

    def delayedInit(x: => Unit) {
        // your excution logic goes here
        x
        cmds map ("Excutintg: " + _) foreach println
    }
}

它的用法是:

代码语言:javascript
复制
class MyCoolScript extends MyMagicTrait {
  val downloader = "wget"

  $"mkdir /tmp/test"
  $"cd /tmp/test" 
  $"""
    $downloader some-url
    $downloader another-url
   """ 
}

new MyCoolScript

这两种解决方案产生相同的输出:

代码语言:javascript
复制
Executing: mkdir /tmp/test
Executing: cd /tmp/test
Executing: wget some-url
Executing: wget another-url
票数 4
EN

Stack Overflow用户

发布于 2012-05-22 19:32:37

代码语言:javascript
复制
class Shell {
  var elems = Vector[String]()
  def >(str: String) = elems :+= str

  def run() = elems.map( whatever )
}

val shell = new Shell

shell> "mkdir /tmp/test.dir"
shell> "cd /tmp/test.dir"
票数 5
EN

Stack Overflow用户

发布于 2012-05-22 20:02:40

这只是对@Debilski和@Tomasz的想法的翻版,以表明您可以很好地将它们结合起来:

代码语言:javascript
复制
trait Magic {
  object shell {
    var lines = IndexedSeq[String]()
    def >(xs: String) { lines ++= xs.split("\n").map(_.trim) }
  }
  def doSomethingWithCommands { shell.lines foreach println }
}

object MyCoolScript extends App with Magic {
  println("this is Scala")

  shell> """
    mkdir /tmp/test
    cd /tmp/test
  """

  doSomethingWithCommands

  shell> """
    wget some-url
  """
}

如果您想将shell与Scala命令结合起来,我不太明白如何获得更少的样板,因为您需要一些东西来显示shell的开始和结束位置。

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/10708136

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档