Groovy виконання команд оболонки


178

Groovy додає executeспосіб Stringзробити виконання снарядів досить простим;

println "ls".execute().text

але якщо трапляється помилка, то результату немає. Чи є простий спосіб усунути як стандартну помилку, так і стандартну? (крім створення куки коду; створити два потоки для читання обох вхідних потоків, а потім за допомогою батьківського потоку дочекатися їх завершення, а потім перетворити рядки назад у текст?)

Було б непогано мати щось на кшталт;

 def x = shellDo("ls /tmp/NoFile")
 println "out: ${x.out} err:${x.err}"

Це посилання корисне. Показує, як запустити команду оболонки з демонстрацією cURL.
Анікет Такур

Відповіді:


207

Гаразд, вирішив це сам;

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

дисплеї:

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


13
Якщо вам також потрібно встановити змінні середовища для цього процесу, переконайтеся, що загортайте команду в оболонку. Наприклад, запуск команди Perforce з env vars: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 не пов'язаний із питанням про ОП, але я думаю, що сучасні JVM справляються із безперебійною синхронізацією. Тож StringBuffer навряд чи погіршить продуктивність у сценаріях з обмеженими потоками чи стеками.
Павло Грушецький

3
Документи кажуть, що нам слід використовувати waitForProcessOutput () - "Щоб чекати, коли вихід буде повністю спожитий, виклик waitForProcessOutput ()". Джерело: docs.groovy-lang.org/latest/html/groovy-jdk/java/lang/…
Шрікант

4
@srikanth вихідні документи waitForProcess () також говорять "Використовуйте цей метод, якщо вам не байдуже стандартний або помилковий вихід і просто хочете, щоб процес запускався мовчки" - Я хочу, щоб результат
Bob Herrmann,

sout і serr можуть бути недоступними навіть після вичерпного очікуванняForOrKill. Тестується за допомогою актарт замість println. Документи кажуть: "Для цього запускаються дві нитки, тому цей метод повернеться негайно. Нитки не будуть з'єднані () ed, навіть якщо виклик waitFor (). Чекати, коли вихід буде повністю використаний, виклик waitForProcessOutput () . "
сонцестояння333

49

"ls".execute()повертає Processоб'єкт, через що "ls".execute().textпрацює. Ви повинні мати можливість просто прочитати потік помилок, щоб визначити, чи були помилки.

Існує додатковий метод на Processякі дозволяють пропускати , StringBufferщоб отримати текст: consumeProcessErrorStream(StringBuffer error).

Приклад:

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

println proc.text
println b.toString()

Це не працює зі сценарієм Bourn Again Shell! # / Bin / bash,
Рашмі Джайн

1
Якщо ви працюєте зі скриптами bash, ви, ймовірно, викликаєте bash як частину команди: "/ 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()
}

10
+1 Це показує вихід поступово, коли вихід генерується. Що вкрай важливо для тривалого процесу
samarjit samanta

велика частка там @ mholm815
Jimmy Obonyo Abor

2
Щоб скористатися цим рішенням, поставте наступний рядок:runCommand("echo HELLO WORLD")
Miron V

@ mholm815 Як ми можемо схвалити необхідні сценарії з самого конвеєра?
Ронак Патель

25

Я вважаю це більш ідіоматичним:

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"

Як згадується інша публікація, це блокування дзвінків, але оскільки ми хочемо працювати з висновком, це може знадобитися.


24

Щоб додати ще одну важливу інформацію до наведених вище відповідей -

Для процесу

def proc = command.execute();

завжди намагайтеся використовувати

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

а не

def output = proc.in.text;

для захоплення виходів після виконання команд у groovy, оскільки останній є блокуючим викликом ( ТАК питання з причини ).


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]

3
Мені дуже прикро, що людина знайшла час, щоб дати відповідь, і хтось просто спростував її без видимих ​​причин. якщо це спільнота, слід почувати себе зобов’язаним додати коментар (якщо це не дуже очевидна причина, яку будь-який компетентний програміст негайно побачить), пояснюючи супровід.
Амос Бордовиц

6
@AmosBordowitz Багато відповідей отримують зворотній зв'язок. Це добре, це одна голова. Однак це може бути тому, що це код, у якому немає слова пояснення - не завжди добре сприймається.
Кріс Бейкер

@ChrisBaker так чому б не вказати на це? Ви самі не
впевнені,

5
@AmosBordowitz Я не є офіційним пояснювачем пояснення, не можу сказати, чому ні, і зрозуміло, що я не впевнений, оскільки ми говоримо про дії, вжиті іншою особою. Я запропонував одну можливість. Чому б не пояснити голосування, звичайно, чому б не пояснити код у відповіді? У будь-якому випадку, я впевнений, що у нас все буде в порядку.
Кріс Бейкер

1
@ChrisBakerI ніколи не висловлював жодної такої претензії ("але я думаю, ви знаєте краще"). Це річ пристойності, а не знання ...
Амос Бордовиц

-3
command = "ls *"

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

але в разі відмови команди процес припиняється


Звідки береться sh?
styl3r

3
shє частиною шорсткої DSL Дженкінса. Напевно, тут не корисно
Gi0rgi0s

4
Jenkins Groovy DSL! = Groovy
Skeeve

як заявили інші, це частина DSL Дженкінса
jonypony3

Ця відповідь не стосується поставленого питання.
Брендон
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.