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 не имеет отношения к вопросу OP, но я думаю, что современные JVM прекрасно справляются с непреднамеренной синхронизацией. Таким образом, StringBuffer вряд ли снизит производительность в сценариях с ограниченным потоком или стеком.
Павел Грушецкий
3
В документах говорится, что мы должны использовать waitForProcessOutput () - «Чтобы дождаться полной загрузки вывода, вызовите waitForProcessOutput ()». Источник: docs.groovy-lang.org/latest/html/groovy-jdk/java/lang/...
Srikanth
4
@srikanth Выходные документы waitForProcess () также говорят: «Используйте этот метод, если вы не заботитесь о стандартном выводе или выводе ошибок и просто хотите, чтобы процесс выполнялся без вывода сообщений» - я хочу вывод
Боб Херрманн
sout и serr могут быть недоступны даже после waitForOrKill. Протестировано с использованием assert вместо println. Документы говорят: «Для этого запускаются два потока , поэтому этот метод будет возвращен немедленно. Потоки не будут присоединяться (), даже если вызывается waitFor () . Чтобы дождаться, пока выходные данные будут полностью использованы, вызовите waitForProcessOutput () «.
solstice333
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,
Rashmi Jain
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()
}
mholm815
источник
10
+1 Это показывает вывод по мере того, как вывод генерируется .. что чрезвычайно важно для длительного процесса
samarjit samanta
большая доля там @ mholm815
Джимми Обонио Абор
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"

Как упоминается в другом посте, это блокировка вызовов, но поскольку мы хотим работать с выводом, это может быть необходимо.

solstice333
источник
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]
emles-KZ
источник
3
Я действительно раздражен, что человек нашел время, чтобы дать ответ, а кто-то просто отклонил его без видимой причины. если это сообщество, то вы должны почувствовать себя обязанным добавить комментарий (если это не очень очевидная причина, которую сразу заметит любой компетентный программист), объясняющий понижение рейтинга.
Амос
6
@AmosBordowitz Много ответов получить отрицательные отзывы. Все в порядке, это одно отрицание. Тем не менее, это может быть потому, что это код без слов объяснения - не всегда хорошо принят.
Крис Бейкер
@ChrisBaker, так почему бы не указать на это? Вы сами не
Амос
5
@AmosBordowitz Я не являюсь официальным объяснителем отрицательного мнения, я не могу сказать вам, почему нет, и понятно, что я не уверен, так как мы говорим о действиях, предпринятых другим человеком. Я предложил одну возможность. Почему бы не объяснить понижающий голос, конечно, почему бы не объяснить код в ответе? В любом случае, я уверен, что у нас все будет хорошо.
Крис Бейкер
1
@ChrisBaker Я никогда не делал подобных заявлений («но я думаю, вы знаете лучше»). Это вещь порядочности, а не знания ...
Амос
-3
command = "ls *"

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

но в случае сбоя команды процесс завершится

舒何伟
источник
Откуда shберутся?
styl3r
3
shявляется частью Дженкинс заводной DSL. Вероятно, не полезно здесь
Gi0rgi0s
4
Дженкинс Groovy DSL! = Отличный
Skeeve
как уже
говорили
Этот ответ не относится к заданному вопросу.
Брэндон