8

Scala(Javaでもない)に精通していないがShellに精通しているユーザー向けのAPIを開発する必要があります。彼らは基本的に、scalaクラス内にシェルスクリプトを記述します(外部シェルスクリプトを呼び出すこともできますが、やってみてください!また、後で一般的なシェルタスク用の関数をいくつか用意します)。

私は次のようなことを達成したいと思っていました:

1 object MyCoolScript extends MyMagicTrait {
2   $ "mkdir /tmp/test"
3   $ "cd /tmp/test"
4   $ "wget some-url"   
5 }

もっと直接的に言えば、2〜4行目(またはおそらく簡潔ではないバージョン)をMyMagicTraitで処理できるSeq [String]に変換するにはどうすればよいですか?

私はsys.process.stringToProcessについて知っていますが、持っている場合:

object MyCoolScript extends MyMagicTrait {
  "mkdir /tmp/test" !!
  "cd /tmp/test" !!
  "wget some-url" !!  
}

各コマンドの結果を簡潔に取得するにはどうすればよいですか?また、私は$"xxx"表記を望んでいました。

回答の更新を投稿する:

@ debilski、@ tenshi、@ daniel-c-sobralのおかげで、目的の実装に非常に近づくことができました:https ://gist.github.com/2777994

4

6 に答える 6

5
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"
于 2012-05-22T19:32:37.040 に答える
4

Scala 2.10 に付属の文字列補間がここで役立つようです。最初は$、コマンドをすぐに実行するだけの簡単な方法を実装できます。作成するには、次のカスタム メソッドを追加する必要がありますStringContext

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")
            }

    }
} 

これで、次のように使用できます。

import ShellSupport._

val testDir = "/tmp/test"

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

その構文を利用できます(これは唯一の欠点であり、との間にスペースを追加できないことです$"およびコマンド内の文字列補間を利用できます。


それでは、魔法の特性を実装してみましょう。一般的には同じ考えですが、DelayedInitコマンドを適切に定義し、クラスの作成中にそれらを自動的に実行するためにも使用しています。

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
    }
}

そしてそれは使用法です:

class MyCoolScript extends MyMagicTrait {
  val downloader = "wget"

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

new MyCoolScript

これらのソリューションはどちらも同じ出力を生成します。

Executing: mkdir /tmp/test
Executing: cd /tmp/test
Executing: wget some-url
Executing: wget another-url
于 2012-05-22T20:50:31.370 に答える
3

これは、@Debilski と @Tomasz のアイデアをリフして、それらをうまく組み合わせることができることを示しています。

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
  """
}

シェルの開始位置と終了位置を示す何かが必要なため、シェルと Scala コマンドを組み合わせたい場合、ボイラープレートを減らす方法がよくわかりません。

于 2012-05-22T20:02:40.007 に答える
2

ダミーアプローチ

class Shell(commands: String) {
    commands.lines foreach {
        command =>
            //run your command
    }
}

class MyCoolScript extends Shell("""

    mkdir /tmp/test
    cd /tmp/test
    wget some-url

""")
于 2012-05-22T19:05:51.803 に答える
2

これらの結果はいたるところにあります。残念ながら、sys.process では十分ではないと思います。最初に、あなたが要求したことを実行するような方法で、その例を書きましょう:

val result = (
    "mkdir /tmp/test ###
    "cd /tmp/test" ###
    "wget someurl
).lines_!

さて、なぜうまくいかないのか:

まず、cd例外がスローされcdます。実行可能ファイルはありません。これは内部シェル コマンドであるため、シェルのみが実行できます。

次に、うまくいくと仮定cdすると、 では起こりwgetませ/tmp/test。上記の各コマンドは、Java の現在の作業ディレクトリで新たに実行されます。各コマンドを実行するディレクトリを指定できますが、CWD を指定することはできません (「可変」ではありません)。

于 2012-05-22T22:46:42.250 に答える
1

編集:

私はこれを行います:

res = List("mkdir /tmp/test", "cd /tmp/test", "wget some-url").map(_!!)
// res is a List[String] of the output of your commands

次のようなシステムコマンドを実行できます。

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

scala> "pwd"!
/Users/kgann
res0: Int = 0

これをDSLにラップできるはずですが、すでにかなり簡潔です。

于 2012-05-22T19:09:19.133 に答える