Groovy wykonuje polecenia powłoki

178

Groovy dodaje executemetodę Stringdo, aby wykonywanie powłok było dość łatwe;

println "ls".execute().text

ale jeśli wystąpi błąd, nie ma wynikowego wyniku. Czy istnieje łatwy sposób na uzyskanie zarówno błędu standardowego, jak i standardowego? (inne niż tworzenie zestawu kodu; utworzyć dwa wątki w celu odczytania obu strumieni wejściowych, a następnie użyć strumienia nadrzędnego, aby poczekać na ich zakończenie, a następnie przekonwertować ciągi z powrotem na tekst?)

Byłoby miło mieć coś takiego;

 def x = shellDo("ls /tmp/NoFile")
 println "out: ${x.out} err:${x.err}"
Bob Herrmann
źródło
Ten link jest przydatny. Pokazuje, jak uruchomić polecenie powłoki w wersji demonstracyjnej cURL.
Aniket Thakur

Odpowiedzi:

207

Ok, rozwiązałem to sam;

def sout = new StringBuilder(), serr = new StringBuilder()
def proc = 'ls /badDir'.execute()
proc.consumeProcessOutput(sout, serr)
proc.waitForOrKill(1000)
println "out> $sout err> $serr"

wyświetlacze:

out> err> ls: cannot access /badDir: No such file or directory

Bob Herrmann
źródło
13
W przypadku, gdy musisz również ustawić zmienne środowiskowe dla tego procesu, pamiętaj, aby zawinąć polecenie w powłoce. Na przykład uruchomienie polecenia Perforce z vars env:envVars = ["P4PORT=p4server:2222", "P4USER=user", "P4PASSWD=pass", "P4CLIENT=p4workspace"]; workDir = new File("path"); cmd = "bash -c \"p4 change -o 1234\""; proc = cmd.execute(envVars, workDir);
Noam Manos
@paul_sns niezwiązane z kwestią OP, ale myślę, że nowoczesne maszyny JVM radzą sobie dobrze z niezakłóconą synchronizacją. Dlatego jest mało prawdopodobne, aby StringBuffer pogorszył wydajność w scenariuszach ograniczonych wątkami lub stosem.
Pavel Grushetzky
3
Dokumentacja mówi, że powinniśmy używać waitForProcessOutput () - "Aby czekać na pełne wykorzystanie danych wyjściowych, wywołaj waitForProcessOutput ()". Źródło: docs.groovy-lang.org/latest/html/groovy-jdk/java/lang/…
Srikanth
4
@srikanth Dokumenty wyjściowe waitForProcess () również mówią „Użyj tej metody, jeśli nie zależy Ci na standardowym lub błędnym wyjściu i po prostu chcesz, aby proces działał cicho” - Chcę dane wyjściowe
Bob Herrmann
Sout i Serr mogą nie być dostępne nawet po zakończeniu waitForOrKill. Przetestowano przy użyciu assert zamiast println. Dokumenty mówią: „W tym celu uruchamiane są dwa wątki, więc ta metoda zwróci natychmiast. Wątki nie zostaną połączone (), nawet jeśli wywoływana jest funkcja waitFor () . Aby czekać na pełne wykorzystanie danych wyjściowych, wywołaj funkcję waitForProcessOutput () ”.
przesilenie
49

"ls".execute()zwraca Processobiekt, dlatego "ls".execute().textdziała. Powinieneś być w stanie po prostu przeczytać strumień błędów, aby określić, czy wystąpiły jakieś błędy.

Jest to dodatkowy sposób na Processktóre pozwalają przejść StringBufferdo pobierania tekst: consumeProcessErrorStream(StringBuffer error).

Przykład:

def proc = "ls".execute()
def b = new StringBuffer()
proc.consumeProcessErrorStream(b)

println proc.text
println b.toString()
Joshua
źródło
Nie działa ze skryptem Bourn Again Shell! # / Bin / bash,
Rashmi Jain
1
Jeśli pracujesz ze skryptami bash, prawdopodobnie wywołujesz bash jako część polecenia: „/ bin / bash script” .execute ()
Niels Bech Nielsen
32
// a wrapper closure around executing a string                                  
// can take either a string or a list of strings (for arguments with spaces)    
// prints all output, complains and halts on error                              
def runCommand = { strList ->
  assert ( strList instanceof String ||
           ( strList instanceof List && strList.each{ it instanceof String } ) \
)
  def proc = strList.execute()
  proc.in.eachLine { line -> println line }
  proc.out.close()
  proc.waitFor()

  print "[INFO] ( "
  if(strList instanceof List) {
    strList.each { print "${it} " }
  } else {
    print strList
  }
  println " )"

  if (proc.exitValue()) {
    println "gave the following error: "
    println "[ERROR] ${proc.getErrorStream()}"
  }
  assert !proc.exitValue()
}
mholm815
źródło
10
+1 Pokazuje to wyjście przyrostowo w miarę generowania wyjścia… co jest niezwykle ważne w przypadku długotrwałego procesu
samarjit samanta
wielki udział w tym miejscu @ mholm815
Jimmy Obonyo Abor
2
Aby skorzystać z tego rozwiązania, wpisz następującą linię:runCommand("echo HELLO WORLD")
Miron V
@ mholm815 jak możemy zatwierdzić wymagane skrypty z samego potoku?
Ronak Patel
25

Uważam to za bardziej idiomatyczne:

def proc = "ls foo.txt doesnotexist.txt".execute()
assert proc.in.text == "foo.txt\n"
assert proc.err.text == "ls: doesnotexist.txt: No such file or directory\n"

Jak wspomina inny post, są to połączenia blokujące, ale ponieważ chcemy pracować z wyjściem, może to być konieczne.

przesilenie 333
źródło
24

Aby dodać jeszcze jedną ważną informację do powyższych odpowiedzi -

Na proces

def proc = command.execute();

zawsze staraj się użyć

def outputStream = new StringBuffer();
proc.waitForProcessOutput(outputStream, System.err)
//proc.waitForProcessOutput(System.out, System.err)

zamiast

def output = proc.in.text;

przechwytywanie wyjść po wykonaniu poleceń w groovy, ponieważ to drugie jest wywołaniem blokującym ( pytanie SO z powodu ).

Aniket Thakur
źródło
6
def exec = { encoding, execPath, execStr, execCommands ->

def outputCatcher = new ByteArrayOutputStream()
def errorCatcher = new ByteArrayOutputStream()

def proc = execStr.execute(null, new File(execPath))
def inputCatcher = proc.outputStream

execCommands.each { cm ->
    inputCatcher.write(cm.getBytes(encoding))
    inputCatcher.flush()
}

proc.consumeProcessOutput(outputCatcher, errorCatcher)
proc.waitFor()

return [new String(outputCatcher.toByteArray(), encoding), new String(errorCatcher.toByteArray(), encoding)]

}

def out = exec("cp866", "C:\\Test", "cmd", ["cd..\n", "dir\n", "exit\n"])

println "OUT:\n" + out[0]
println "ERR:\n" + out[1]
emles-kz
źródło
3
Jestem naprawdę zirytowany, że dana osoba poświęciła czas na udzielenie odpowiedzi, a ktoś po prostu zagłosował za nią bez wyraźnego powodu. jeśli jest to społeczność, należy czuć się zobowiązanym do dodania komentarza (chyba że jest to bardzo oczywisty powód, który każdy kompetentny programista od razu zobaczy) wyjaśniający głos przeciw.
Amos Bordowitz
6
@AmosBordowitz Wiele odpowiedzi jest negatywnych. W porządku, jeden głos przeciw. To powiedziawszy, może to być spowodowane tym, że jest to kod bez słowa wyjaśnienia - nie zawsze dobrze przyjęty.
Chris Baker
@ChrisBaker, więc dlaczego nie wskazać tego? Ty sam nie jesteś
pewien,
5
@AmosBordowitz Nie jestem oficjalnym tłumaczem głosów przeciw, nie mogę powiedzieć, dlaczego nie, i zrozumiałe jest, że nie jestem pewien, ponieważ mówimy o działaniu podjętym przez inną osobę. Zaproponowałem jedną możliwość. Dlaczego nie wyjaśnić negatywnego głosu, jasne, dlaczego nie wyjaśnić kodu w odpowiedzi? W każdym razie jestem pewien, że wszyscy będziemy w porządku.
Chris Baker
1
@ChrisBaker Nigdy nie wysunąłem żadnego takiego twierdzenia („ale myślę, że wiesz lepiej”). To kwestia przyzwoitości, a nie wiedzy ...
Amos Bordowitz
-3
command = "ls *"

def execute_state=sh(returnStdout: true, script: command)

ale jeśli polecenie nie powiedzie się, proces zostanie zakończony

舒何伟
źródło
Skąd się shbierze?
styl3r
3
shjest częścią groovy DSL Jenkins. Prawdopodobnie nie przydatne tutaj
Gi0rgi0s
4
Jenkins Groovy DSL! = Groovy
Skeeve
jak powiedzieli inni, jest to część Jenkins DSL
jonypony3
Ta odpowiedź nie ma zastosowania do zadanego pytania.
Brandon