2014-01-23 4 views
8

В POSIX awk, как мне получить статус выхода (код возврата) от commandпосле обработки его вывода через command | getline var? Я хочу, чтобы мой awk-скрипт был exit 1, если command вышел с ненулевым статусом выхода.Как получить статус выхода команды в конвейере getline?

Например, предположим, что у меня был сценарий AWK именем foo.awk который выглядит следующим образом:

function close_and_get_exit_status(cmd) { 
    # magic goes here... 
} 
BEGIN { 
    cmd = "echo foo; echo bar; echo baz; false" 
    while ((cmd | getline line) > 0) 
     print "got a line of text: " line 
    if (close_and_get_exit_status(cmd) != 0) { 
     print "ERROR: command '" cmd "' failed" | "cat >&2" 
     exit 1 
    } 
    print "command '" cmd "' was successful" 
} 

я хочу следующее случиться:

$ awk -f foo.awk 
got a line of text: foo 
got a line of text: bar 
got a line of text: baz 
ERROR: command 'echo foo; echo bar; echo baz; false' failed 
$ echo $? 
1 

Согласно POSIX specification for awk, command | getline возвращает 1 для успешного ввода, ноль для конца файла и -1 для ошибки. Это не ошибка, если command выходит с ненулевым статусом выхода, поэтому это невозможно использовать, чтобы проверить, выполнено ли command и не удалось.

Аналогично, close() не может использоваться для этой цели: close() возвращает ненулевое значение, только если сбой завершен, а не если соответствующая команда возвращает ненулевой статус выхода. (В простаке, close(command) возвращает статус выхода command. Это поведение, я хотел бы, но я думаю, что это нарушает спецификацию POSIX и не все реализации AWK ведут себя таким образом.)

Функция AWK system() возвращает статус выхода команды, но насколько я могу судить, нет способа использовать getline.

+1

+1 +1 вопрос. обратитесь к http://docs.freebsd.org/info/gawk/gawk.info.Getline.html, вы можете подумать использовать «ERRNO», чтобы помочь вам получить статус выхода. 'getline' возвращает 1, если находит запись, и 0, если встречается конец файла . Если в получении записи есть некоторая ошибка, такая как файл, который не может быть открыт, тогда 'getline' возвращает -1. В этом случае gawk устанавливает переменную 'ERRNO' в строку, описывающую ошибку , которая произошла. – BMW

+0

@BMW: Спасибо за комментарий. К сожалению, у POSIX awk нет 'ERRNO'. Кроме того, даже в gawk команда, возвращающая ненулевое значение, не возвращает 'getline' значение -1. –

+0

Сделайте это http://awk.freeshell.org/AllAboutGetline вместо awk.info –

ответ

3

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

$ cat tst.awk  
BEGIN { 
    cmd = "echo foo; echo bar; echo baz; false" 

    mod = cmd "; echo \"$?\"" 
    while ((mod | getline line) > 0) { 
     if (numLines++) 
      print "got a line of text: " prev 
     prev = line 
    } 
    status = line 
    close(mod) 

    if (status != 0) { 
     print "ERROR: command '" cmd "' failed" | "cat >&2" 
     exit 1 
    } 
    print "command '" cmd "' was successful" 
} 

$ awk -f tst.awk 
got a line of text: foo 
got a line of text: bar 
got a line of text: baz 
ERROR: command 'echo foo; echo bar; echo baz; false' failed 
$ echo $? 
1 

В случае, если кто-то читает это и рассматривает возможность использования GetLine, убедитесь, что вы читали http://awk.freeshell.org/AllAboutGetline и полностью понять все предостережения и последствия делать это первым.

+0

Единственным недостатком этого подхода является то, что обработка задерживается линией. Если 'cmd' выводит текст медленно и быстро реагирует (например, нечасто вывод из системы обнаружения вторжений, которая должна немедленно инициировать изменение брандмауэра или уведомление по электронной почте), эта задержка может быть проблемой. Для таких приложений решение в моем ответе может быть более подходящим. Но такие приложения встречаются редко, и я бы поставил под вопрос использование awk с 'getline' для такого приложения, поэтому простота этого ответа делает его лучше в целом. –

+0

Я обновил свой ответ, чтобы просто включить это окончательное решение. Да, обработка задерживается на 1 строку, и я согласен, что почти наверняка отлично подходит для любого разумного awk-приложения. Если нет, вы всегда можете вставлять какие-то сумасшедшие строки контрольных символов или что-то вокруг состояния, когда оно эхом, а затем проверять это, а если не обнаруживать процесс текущей строки, а не задерживать ее по строке. –

2

Не идеальное решение, но вы можете сделать:

"command || echo failure" | getline var; ... if(var == "failure") exit; 

Существует некоторая неопределенность в том, что вы должны выбрать строку «провал» таким образом, чтобы повелевать никогда не может генерировать ту же строку, но возможно, это адекватное обходное решение.

+0

Спасибо за предложение. Я надеялся на более общее решение, но это будет сделано для быстрого и грязного сценария. –

+0

+1 для простоты; предложение для строки, которая вряд ли встречается в текстовых файлах: 'printf '\\ a'', а затем test' if (var == "\ a") ' – mklement0

1

Ниже ужасающе сложно, но:

  • является POSIX совместимым (в основном - fflush() еще не в стандарте POSIX, but it will be и это широко распространено)
  • общее (работает независимо от того, какой выход выдает команда)
  • не вводит никакой задержки обработки. Принятый ответ на этот вопрос делает линию доступной только после того, как команда следующая строка. Если команда медленно выводит строки и быстро реагирует (например, случайные события, напечатанные системой IDS, которая должна инициировать изменение брандмауэра или уведомление по электронной почте), этот ответ может быть более уместным, чем принятый ответ.

Основной подход состоит в том, чтобы эхоиздать статус выхода/возвращаемое значение после завершения команды. Если эта последняя строка не равна нулю, выйдите из awk-скрипта с ошибкой. Чтобы код не ошибался в строке вывода текста командой для статуса выхода, каждая строка текста, выводимая командой, добавляется буквой, которая позже удаляется.

function stderr(msg) { print msg | "cat >&2"; } 
function error(msg) { stderr("ERROR: " msg); } 
function fatal(msg) { error(msg); exit 1; } 

# Wrap cmd so that each output line of cmd is prefixed with "d". 
# After cmd is done, an additional line of the format "r<ret>" is 
# printed where "<ret>" is the integer return code/exit status of the 
# command. 
function safe_cmd_getline_wrap(cmd) { 
    return             \ 
     "exec 3>&1;"          \ 
     "ret=$("           \ 
     " exec 4>&1;"         \ 
     " { ("cmd") 4>&-; echo $? >&4; } 3>&- |"  \ 
     " awk '{print\"d\"$0;fflush()}' >&3 4>&-;"  \ 
     ");"            \ 
     "exec 3>&-;"          \ 
     "echo r${ret};" 
} 

# like "cmd | getline line" except: 
# * if getline fails, the awk script exits with an error 
# * if cmd fails (returns non-zero), the awk script exits with an 
#  error 
# * safe_cmd_getline_close(cmd) must be used instead of close(cmd) 
function safe_cmd_getline(cmd,  wrapped_cmd,ret,type) { 
    wrapped_cmd = safe_cmd_getline_wrap(cmd) 
    ret = (wrapped_cmd | getline line) 
    if (ret == -1) fatal("failed to read line from command: " cmd) 
    if (ret == 0) return 0 
    type = substr(line, 1, 1) 
    line = substr(line, 2) 
    if (type == "d") return 1 
    if (line != "0") fatal("command '" cmd "' failed") 
    return 0 
} 
function safe_cmd_getline_close(cmd) { 
    if (close(safe_cmd_getline_wrap(cmd))) fatal("failed to close " cmd) 
} 

используется выше, как это:

cmd = "ls no-such-file" 
while (safe_cmd_getline(cmd)) { 
    print "got a line of text: " line 
} 
safe_cmd_getline_close(cmd) 
+0

Вы ответили на свой вопрос? – aks

+1

@aks: Да, я понял это после того, как спросил. Мое решение не очень красивое, поэтому я надеюсь, что появится более умный ответ. Ответ на свой вопрос может показаться немного странным, но на самом деле его поощряют: https://stackoverflow.com/help/self-answer –

+0

Какая проблема заключается в том, что awk-скрипт пытается решить? Может быть, это можно сделать прямо в bash? – aks

Смежные вопросы