2013-03-14 20 views
29

Sto lavorando con la libreria Scala scala.sys.process.Scala Processo - Cattura uscita standard e codice di uscita

So che posso acquisire il codice di uscita con ! e l'output con !! ma cosa succede se voglio catturare entrambi?

Ho visto questa risposta https://stackoverflow.com/a/6013932/416338 che sembra promettente, ma mi chiedo se c'è un solo rivestimento e mi manca qualcosa.

+2

RE: "anche alla ricerca di un modo semplice per fare questo ...". Pubblicare un premio non equivale a sventolare una bacchetta magica ;-). Non ci sono sempre risposte semplici alle sfide che affrontiamo. –

+2

In effetti, pubblicare una taglia * è * agitando una bacchetta magica. Ottieni una quantità magica di attenzione alla domanda. – javadba

risposta

15

È possibile utilizzare ProcessIO. Avevo bisogno di qualcosa di simile in un test Specs2, dove ho dovuto controllare il valore di uscita, così come l'uscita di un processo a seconda dell'ingresso sul stdin (in e out sono di tipo 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 capito che potrebbe aiutarti. Nell'esempio sto ignorando ciò che viene mai da stderr.

+0

ProcessIO è di classe interna - è inaccessibile –

+1

Non pensare. Guarda i [documenti] (http: //www.scala- lang.org/api/2.11.6/index.html#scala.sys.process.ProcessIO). –

2

La risposta fornita da "Alex Cruise" nel collegamento è abbastanza concisa, escludendo le prestazioni peggiori.

Si potrebbe estendere sys.process.ProcessLogger per gestire il

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

internamente, con getter per l'out.reverse e risultati err.reverse.

20

ho il seguente metodo di utilità per l'esecuzione di comandi:

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

Come si può vedere, cattura stdout, stderr e il codice risultato.

+0

@cevaris - testato con il seguente script, ottenuto in uscita entrambe le volte: http://pastebin.com/myvfwZWQ Che cosa usi per testare questo? – Rogach

+0

Lo stavo eseguendo in un modo complesso, molto probabilmente un errore dell'utente se funziona per te. Andando a ridimensionare il mio commento per ora. – cevaris

5

L'utilizzo su una sola riga di BasicIO o ProcessLogger è accattivante.

scala> val sb = new StringBuffer 
sb: StringBuffer = 

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

scala> sb 
res1: StringBuffer = ... 

o

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

A seconda di cosa si intende per cattura, forse siete interessati a uscita a meno che il codice di uscita è diverso da zero. In tal caso, gestisci l'eccezione.

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

Penso che non sia del tutto sicuro assumere che il messaggio di errore sarà sempre esattamente 'Valore di uscita diverso da zero: (...)' ... – ValarDohaeris

+0

Questo è un difetto nell'API, per sicurezza, ma fortunatamente è open source e noterai se tenteranno mai di cambiarlo e dirai, Ehi, la gente si affida a quel messaggio! e lo annulleranno. Sto cercando di ricordare se ho mai fatto affidamento sull'uscita diversa da zero di un processo essendo un tre invece di due o diciassette. –

+0

Con "non del tutto sicuro" probabilmente intendevi "non del tutto sicuro". Ovviamente, l'errore non deve essere esattamente quello, ma includere solo il modello. –

9

È possibile specificare un flusso di output che cattura il testo:

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

Questo è bello! Incerto perché non lo amo. – javadba

+1

Per me il 'val code = (" volname "#> os !! 'La riga si blocca solo se il comando fallisce – BenFradet

+0

tuttavia funziona se sostituisco' ByteArrayOutputStream' con un 'File' – BenFradet