2012-05-22 2 views
8

スケーラに慣れていない(Javaではなく)、シェルに精通していないユーザのためにAPIを開発する必要があります。彼らは基本的にスカラクラスの中にシェルスクリプトを書いています(私は外部シェルスクリプトを呼び出すことができると知っていますが、やって来ます!また、後で一般的なシェルタスクのためのいくつかの機能があります)。余分な定型文を使用してコマンドラインのためのスカラでDSLを作成する方法

私はのような何かを達成するために期待していた:どのように私はMyMagicTraitに処理することができ、配列[文字列]にライン2-4(または多分より少ない簡潔なバージョン)を変えることができ、

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

がより直接的ビーイングを?

私は約sys.process.stringToProcessを知っているが、私は持っている場合:

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

私は簡潔な方法で、各コマンドの結果を取得できますか?また、私は$ xxx表記を期待していました。

ポストアップデート回答:

@debilskiのおかげで、@tenshiと@ダニエル・C-ソブラルは、私は希望の実装に非常に近くまで来ることができました:https://gist.github.com/2777994

+2

コードで行番号を取得すると、私はそれが好きです。 5に数えることはとても難しいです!そしてそれは私のsedの経験を常に新鮮に保ちます。 –

+0

私は2行目から4行目を参照していたので、これを行いました。今はsedの使用を見ないでください:)。 –

+1

ところで、 '$'は使わないでください。あなたはそれを使用してコードを書くことができますが、法的なコードではありません。 –

答えて

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

""") 
+0

このアプローチの問題は、コマンドライン間またはその一部としてスカラ文を使用できないことです。たぶんscalaの2.10の文字列の補間が私に役立つかもしれません。それでも私はより良い解決策を探し求めていました。ありがとう。 –

+0

@JhonnyEverson:もっと良い解決策を望んでいます:-)。 –

1

EDIT:

私はこれを行うだろう

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

あなたは、システムを実行することができますので、のようなコマンド:あなたがDSLにこれをラップすることができるはず

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

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

が、それはすでにかなり簡潔です。

+0

ありがとうございますが、私の質問は、文字列をグループ化してリストにして処理できるようにする方法です。私は各プロセスの結果を得る必要があります –

+0

このソリューションの問題は、スクリプトがすぐに判読できなくなることです。私は10-50行のスクリプトについて話しています。私はリストの要素に1行+カンマを使うことができると思いますが、それはあまりきれいではありません。 –

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" 
+0

今話している!シェルの代わりに$を呼び出すと、$> ""があります。今私はそれを自動的に適用する方法が必要です。ありがとう。 –

3

これはちょうどあなたがうまくそれらを組み合わせることができます表示するにはDebilskiのとトマシュのアイデア@ @上のリフさ:

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コマンドと組み合わされているのは、シェルの開始と終了の場所を示すためです。

2

これらの結果は、すべての場所です。悲しいかな、私はsys.processはあなたのために十分ではないと思います。それが動作しない理由を、今

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

::さんは最初、それはあなたが尋ねないように、その一例を書いてみましょうない

まず、cdが例外をスローします - 何があります実行可能ファイルcd。これは内部のシェルコマンドなので、シェルだけが実行できます。

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

+0

私はそれについてはわかりませんでしたが、私はそれを念頭に置いていました。コマンドを動作させるために必要な調整を行うことを考えていました。 –

関連する問題