2013-03-14 11 views
29

Pracuję z biblioteką Scala scala.sys.process.Scala Process - Capture Standard Out and Exit Code

Wiem, że mogę przechwytywać kod wyjścia z ! i wyjście z !!, ale co jeśli chcę przechwycić oba?

Widziałem tę odpowiedź https://stackoverflow.com/a/6013932/416338, która wygląda obiecująco, ale zastanawiam się, czy istnieje jedna linijka i czegoś brakuje.

+2

Re: „poszukuje także prosty sposób to zrobić ...”. Publikowanie nagrody nie jest tym samym, co machanie czarodziejską różdżką ;-). Nie zawsze są proste odpowiedzi na wyzwania, przed którymi stoimy. –

+2

Faktycznie ogłoszenie nagrody * oznacza * wymachiwanie czarodziejską różdżką. Dostajesz magiczną uwagę na to pytanie. – javadba

Odpowiedz

15

Można użyć ProcessIO. Potrzebowałem czegoś takiego w Specs2 Test, gdzie musiałem sprawdzić wartość wyjściową, a także wyjścia procesu w zależności od wejścia na stdin (in i out są typu String):

"the operation" should { 
    f"return '$out' on input '$in'" in { 
    var res = "" 
    val io = new ProcessIO(
     stdin => { stdin.write(in.getBytes) 
        stdin.close() }, 
     stdout => { res = convertStreamToString(stdout) 
        stdout.close() }, 
     stderr => { stderr.close() }) 
    val proc = f"$operation $file".run(io) 
    proc.exitValue() must be_==(0) 
    res must be_==(out) 
    } 
} 

I Pomyślałem, że może ci pomóc. W tym przykładzie ignoruję to, co kiedykolwiek pochodzi z stderr.

+0

ProcessIO jest klasą wewnętrzną - jest niedostępna. –

+1

Nie sądzę. Spójrz na [docs] (http: //www.scala- lang.org/api/2.11.6/index.html#scala.sys.process.ProcessIO) –

2

Odpowiedź udzielona przez "Alex Cruise" w twoim linku jest dość zwięzła, co wyklucza gorsze wyniki.

Można przedłużyć sys.process.ProcessLogger zarządzać

var out = List[String]() 
var err = List[String]() 

wewnętrznie, z pobierające dla out.reverse i err.reverse wyników.

20

Mam następujący sposób narzędzie służące do wykonywania poleceń:

import sys.process._ 
def runCommand(cmd: Seq[String]): (Int, String, String) = { 
    val stdoutStream = new ByteArrayOutputStream 
    val stderrStream = new ByteArrayOutputStream 
    val stdoutWriter = new PrintWriter(stdoutStream) 
    val stderrWriter = new PrintWriter(stderrStream) 
    val exitValue = cmd.!(ProcessLogger(stdoutWriter.println, stderrWriter.println)) 
    stdoutWriter.close() 
    stderrWriter.close() 
    (exitValue, stdoutStream.toString, stderrStream.toString) 
} 

Jak widać, to przechwytuje stdout, stderr i kod wynikowy.

+0

@cevaris - testowany za pomocą poniższego skryptu, otrzymywał dane wyjściowe za każdym razem: http://pastebin.com/myvfwZWQ Do czego służy to testowanie? – Rogach

+0

Wykonuje to w złożony sposób, najprawdopodobniej błąd użytkownika, jeśli działa. Zmieniam teraz swój komentarz. – cevaris

5

Jednoskładkowe użycie BasicIO lub ProcessLogger jest atrakcyjne.

scala> val sb = new StringBuffer 
sb: StringBuffer = 

scala> ("/bin/ls /tmp" run BasicIO(false, sb, None)).exitValue 
res0: Int = 0 

scala> sb 
res1: StringBuffer = ... 

lub

scala> import collection.mutable.ListBuffer 
import collection.mutable.ListBuffer 

scala> val b = ListBuffer[String]() 
b: scala.collection.mutable.ListBuffer[String] = ListBuffer() 

scala> ("/bin/ls /tmp" run ProcessLogger(b append _)).exitValue 
res4: Int = 0 

scala> b mkString "\n" 
res5: String = ... 

W zależności od tego, co masz na myśli przez porwanie, może jesteś zainteresowany wyjścia, chyba że kod wyjścia jest niezerowy. W takim przypadku zajmij się wyjątkiem.

scala> val re = "Nonzero exit value: (\\d+)".r.unanchored 
re: scala.util.matching.UnanchoredRegex = Nonzero exit value: (\d+) 

scala> Try ("./bomb.sh" !!) match { 
    | case Failure(f) => f.getMessage match { 
    | case re(x) => println(s"Bad exit $x") 
    | } 
    | case Success(s) => println(s) 
    | } 
warning: there were 1 feature warning(s); re-run with -feature for details 
Bad exit 3 
+0

Myślę, że nie jest wcale bezpiecznie założyć, że komunikat o błędzie będzie zawsze dokładnie "Niezerowa wartość wyjścia: (...)' ... – ValarDohaeris

+0

To jest wadą API, na pewno, ale na szczęście jest to open source i zauważysz, że kiedykolwiek spróbują to zmienić, a powiesz: Hej, ludzie polegają na tej wiadomości! i powrócą. Próbuję sobie przypomnieć, czy kiedykolwiek opierałem się na niezerowym wyjściu z procesu, który jest trzy zamiast dwóch lub siedemnastu. –

+0

Przez "wcale nie bezpieczny" prawdopodobnie masz na myśli "nie do końca bezpieczny". Oczywiście błąd nie musi być dokładnie taki, ale uwzględnia tylko wzorzec. –

9

Można określić strumień wyjściowy, który łapie tekst:

import sys.process._ 
val os = new java.io.ByteArrayOutputStream 
val code = ("volname" #> os).! 
os.close() 
val opt = if (code == 0) Some(os.toString("UTF-8")) else None 
+0

To jest miłe! Niepewne, dlaczego nie kocham tego ... – javadba

+1

Dla mnie 'val code = (" volname "#> os).! 'linia po prostu zawiesza się, jeśli polecenie się nie powiedzie – BenFradet

+0

działa jednak, jeśli zastąpię' ByteArrayOutputStream' przez 'Plik' – BenFradet