2012-05-22 13 views
8

के साथ कमांड लाइनों के लिए स्केल में डीएसएल कैसे बनाएं, मुझे स्केल (न तो जावा) से परिचित नहीं बल्कि शैल से परिचित उपयोगकर्ताओं के लिए एक एपीआई विकसित करने की आवश्यकता है। वे मूल रूप से स्केल क्लास के अंदर खोल स्क्रिप्ट लिखेंगे (मुझे पता है कि मैं केवल बाहरी शैल स्क्रिप्ट्स को कॉल कर सकता हूं, लेकिन आओ! इसके अलावा, बाद में हमारे पास सामान्य शैल कार्यों के लिए कुछ फ़ंक्शन होंगे)।न्यूनतम अतिरिक्त बॉयलरप्लेट

मैं की तरह कुछ हासिल करने के लिए उम्मीद कर रहा था:

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

अधिक प्रत्यक्ष होने के नाते, मैं कैसे में Seq [स्ट्रिंग] लाइनों 2-4 (या एक संभवतः कम संक्षिप्त संस्करण) को बदल सकते हैं कि मैं MyMagicTrait में संसाधित कर सकते हैं ?

मैं के बारे में sys.process.stringToProcess जानते हैं, लेकिन अगर मेरे पास है:

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

कैसे मैं एक संक्षिप्त तरीके से प्रत्येक आदेश का परिणाम मिल सकता है? भी, मैं $ "xxx" नोटेशन की उम्मीद कर रहा था।

पोस्ट अपडेट जवाब:

धन्यवाद, @debilski को @tenshi और @ डैनियल-सी Sobral मैं आने के लिए सक्षम था एक बहुत ही वांछित कार्यान्वयन के करीब: https://gist.github.com/2777994

+2

मुझे यह पसंद है जब हमें कोड में लाइन नंबर मिलते हैं। 5 तक गिनना इतना मुश्किल है! और यह मेरे sed-experience हमेशा ताजा रहता है। –

+0

मैंने ऐसा इसलिए किया क्योंकि मैं लाइनों 2-4 का जिक्र कर रहा था। अब sed के लिए उपयोग नहीं देखें :)। –

+1

वैसे, '$' का उपयोग न करें। आप इसका उपयोग कर कोड लिख सकते हैं, लेकिन यह कानूनी कोड नहीं है, और यह बिना किसी सूचना के तोड़ सकता है। –

उत्तर

4

लगता है कि स्कैला 2.10 के साथ आने वाली स्ट्रिंग इंटरपोलेशन आपको यहां मदद कर सकता है। सबसे पहले आप सरल $ विधि को कार्यान्वित कर सकते हैं, जो तुरंत आदेश को निष्पादित करता है। ताकि इसे बनाने के लिए आप StringContext पर इस कस्टम विधि जोड़ने की जरूरत:

import ShellSupport._ 

val testDir = "/tmp/test" 

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

आप लाभ ले सकते हैं की यह वाक्य रचना है (यह है केवल नकारात्मक पक्ष यह है, यह है:

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

    } 
} 

अब आप इसे इस तरह उपयोग कर सकते हैं कि आप $ और " के बीच स्थान नहीं जोड़ सकते हैं) और कमांड के भीतर इंटरपोलेशन स्ट्रिंग कर सकते हैं।


अब अपने जादू की विशेषता को लागू करने का प्रयास करें। यह आम तौर पर एक ही विचार है, लेकिन मैं आदेशों को सही तरीके से परिभाषित करने के लिए 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 
    } 
} 

और यह उपयोग है:

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

इस दृष्टिकोण के साथ समस्या यह है कि मेरे पास कमांड लाइनों या उनमें से कुछ के बीच स्केल स्टेटमेंट नहीं हो सकते हैं। शायद स्कैला की 2.10 स्ट्रिंग इंटरपोलेशन मुझे इसके साथ मदद कर सकती है। फिर भी मैं एक बेहतर समाधान के लिए hopping था। धन्यवाद। –

+0

@ जॉनी एवरसन: मैं एक बेहतर समाधान की भी उम्मीद कर रहा हूं :-)। –

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 

आप एक डीएसएल में इस रैप करने के लिए सक्षम होना चाहिए, लेकिन यह पहले से ही बहुत संक्षिप्त है।

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

मैं वास्तव में नहीं दिख रहा है यदि आप चाहते हैं कि कैसे आप किसी भी कम बॉयलरप्लेट मिल सकता है:

+0

धन्यवाद लेकिन मुझे पता है कि, मेरा सवाल यह है कि मैं स्ट्रिंग्स की लाइनों को कैसे समूहीकृत कर सकता हूं और उन्हें एक सूची में बदल सकता हूं ताकि मैं उन्हें संसाधित कर सकूं। मुझे प्रत्येक प्रक्रिया का परिणाम प्राप्त करने की आवश्यकता है –

+0

इस समाधान के साथ समस्या यह है कि स्क्रिप्ट जल्दी से अपठनीय हो जाएंगी। मैं लिपि की 10-50 लाइनों के बारे में बात कर रहा हूं। मुझे लगता है कि मैं सूची के तत्वों के लिए एक लाइन + कॉमा का उपयोग कर सकता हूं, लेकिन यह बहुत साफ नहीं है। –

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 के @ और @ टोमाज़ के विचारों पर riffing है स्केल कमांड के साथ खोल को गठबंधन करें, क्योंकि आपको यह दिखाने के लिए कुछ चाहिए कि खोल कहां शुरू हो रहा है और समाप्त हो रहा है।

2

ये परिणाम हर जगह हैं:

class MyCoolScript extends MyMagicTrait { 
    val downloader = "wget" 

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

new MyCoolScript 

इन समाधानों में से दोनों एक ही उत्पादन का उत्पादन। हां, मुझे नहीं लगता कि sys.process आपके लिए पर्याप्त होगा।

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

अब, यह क्यों काम नहीं करता है: का पहला कि उदाहरण के बाहर लिखते हैं, इस तरह से यह है कि तुम क्या करने के लिए कहा है कि

पहले, cd एक अपवाद फेंक देंगे - कोई cd निष्पादन योग्य। यह एक आंतरिक खोल कमांड है, इसलिए केवल खोल इसे निष्पादित कर सकता है।

इसके बाद, cd संभालने काम करेगा, wgetनहीं/tmp/test में क्या होगा। उपर्युक्त प्रत्येक आदेश जावा की वर्तमान कार्यशील निर्देशिका में नया निष्पादित किया गया है। आप निर्देशिका निर्दिष्ट कर सकते हैं जिसमें प्रत्येक कमांड को निष्पादित किया जाना है, लेकिन आपके पास सीडब्ल्यूडी नहीं है (यह "mutable" नहीं है)।

+0

मुझे इसके बारे में निश्चित नहीं था लेकिन मुझे यह दिमाग में था। मैं काम करने के लिए आदेशों के लिए आवश्यक समायोजन करने की सोच रहा था। –

संबंधित मुद्दे