Подтвердить что ты не робот

Тройник и статус выхода

существует альтернатива "tee", которая захватывает STDOUT/STDERR исполняемой команды и выходит с тем же статусом выхода, что и обработанная команда. Что-то вроде следующего:

eet -a some.log -- mycommand --foo --bar

Где "eet" - это воображаемая альтернатива "tee":) (-a означает append, - отделяет захваченную команду). Не должно быть сложно взломать такую ​​команду, но, возможно, она уже существует, и я не знал об этом?

Спасибо.

4b9b3361

Ответ 1

Здесь eet. Работает с каждым Bash, я могу взять свои руки, от 2.05b до 4.0.

#!/bin/bash
tee_args=()
while [[ $# > 0 && $1 != -- ]]; do
    tee_args=("${tee_args[@]}" "$1")
    shift
done
shift
# now ${tee_args[*]} has the arguments before --,
# and $* has the arguments after --

# redirect standard out through a pipe to tee
exec | tee "${tee_args[@]}"

# do the *real* exec of the desired program
exec "[email protected]"

(pipefail и $PIPESTATUS хороши, но я помню, как они были представлены в 3.1 или около того.)

Ответ 2

Это работает с bash:

(
  set -o pipefail
  mycommand --foo --bar | tee some.log
)

Скобки заключаются в том, чтобы ограничить эффект pipefail только одной командой.

На странице руководства bash (1):

Возвратный статус конвейера - это статус выхода последней команды, если не включена опция pipefail. Если параметр pipefail включен, статус возврата конвейера - это значение последней (самой правой) команды для выхода с ненулевым статусом или ноль, если все команды успешно завершены.

Ответ 3

Нашел пару интересных решений здесь http://www.perlmonks.org/?node_id=597613.

1) Существует переменная $PIPESTATUS, доступная в bash:

   false | tee /dev/null
   [ $PIPESTATUS -eq 0 ] || exit $PIPESTATUS

2) И простейший прототип "eet" в perl может выглядеть следующим образом:

   open MAKE, "command 2>&1 |" or die;
   open (LOGFILE, ">>some.log") or die;
   while (<MAKE>) { print LOGFILE $_; print }
   close MAKE; # to get $?
   my $exit = $? >> 8;
   close LOGFILE;

Ответ 4

Korn shell, ВСЕ в 1 строке:

foo; RET_VAL=$?; if test ${RET_VAL} != 0;then echo $RET_VAL; echo Error occurred!>/tmp/out.err;exit 2;fi |tee >>/tmp/out.err ; if test ${RET_VAL} != 0;then exit $RET_VAL;fi

Ответ 5

Это то, что я считаю лучшим чистым решением Bourne-shell для использования в качестве базы, на которой вы могли бы построить свой "eet":

# You want to pipe command1 through command2:
exec 4>&1
exitstatus=`{ { command1; printf $? 1>&3; } | command2 1>&4; } 3>&1`
# $exitstatus now has command1 exit status.

Я думаю, что это лучше всего объяснить изнутри - command1 выполнит и напечатает свой обычный вывод на stdout (дескриптор файла 1), а затем, как только это будет сделано, printf выполнит и распечатает код выхода command1 на своем stdout, но этот stdout перенаправляется в дескриптор файла 3.

В то время как command1 запущен, его stdout передается по команде в command2 (вывод printf никогда не приводит к команде2, потому что мы отправляем его в дескриптор файла 3 вместо 1, что и читает труба). Затем мы перенаправляем вывод command2 в дескриптор файла 4, так что он также остается вне файлового дескриптора 1 - потому что мы хотим, чтобы дескриптор файла 1 был освобожден немного позже, потому что мы будем выводить вывод printf на дескриптор файла 3 обратно в дескриптор файла 1 - потому что то, что подменю команды (backticks), будет захватывать, и то, что будет помещено в переменную.

Последний бит магии состоит в том, что первый exec 4>&1 мы сделали как отдельную команду - он открывает файловый дескриптор 4 как копию внешнего командного файла stdout. Подстановка команд будет захватывать все, что написано на стандартном уровне с точки зрения команд внутри него, но, поскольку вывод command2 будет обрабатывать дескриптор 4 до подстановки команды, подстановка команды не захватывает его, после того, как он "выйдет" из подстановки команды, он по-прежнему остается в общем дескрипторе файла script.

(exec 4>&1 должна быть отдельной командой, потому что многим обычным оболочкам это не нравится, когда вы пытаетесь записать в дескриптор файла внутри подстановки команд, который открывается во внешней команде, которая использует подстановка. Так что это самый простой переносной способ сделать это.)

Вы можете смотреть на него менее техничным и более игривым способом, как если бы выходы команд перескакивали друг с другом: command1 pipe to command2, то вывод printf перескакивает по команде 2, так что command2 не поймает его, а затем команда 2 выводит переходы и вытеснения из командной подстановки так же, как printf приземляется как раз вовремя, чтобы получить захват подстановкой, так что она попадает в переменную, а вывод command2 продолжает свой веселый путь, записываемый на стандартный вывод, как и в обычной трубе.

Кроме того, как я понимаю, $? будет по-прежнему содержать код возврата второй команды в канале, поскольку назначения переменных, подстановки команд и составные команды эффективно прозрачны для кода возврата команды внутри них, поэтому статус возврата команды2 должен быть распространен.

Предостережение состоит в том, что возможно, что команда1 в какой-то момент закончит использование файловых дескрипторов 3 или 4 или что команда2 или любая из последующих команд будет использовать файловый дескриптор 4, поэтому для обеспечения более надежной работы выполните следующие действия:

exec 4>&1
exitstatus=`{ { command1 3>&-; printf $? 1>&3; } 4>&- | command2 1>&4; } 3>&1`
exec 4>&-

Обратите внимание, что я использую составные команды в моем примере, но подоболочки (используя ( ) вместо { } также будут работать, но могут быть менее эффективными.)

Команды наследуют дескрипторы файлов из процесса, который запускает их, поэтому вся вторая строка наследует дескриптор файла четыре, а составная команда, за которой следует 3>&1, наследует дескриптор файла три. Таким образом, 4>&- гарантирует, что внутренняя команда соединения не будет наследовать файловый дескриптор четыре, а 3>&- не будет наследовать дескриптор файла три, поэтому command1 получает "более чистую", более стандартную среду. Вы также можете перемещать внутренний 4>&- рядом с 3>&-, но я думаю, почему бы не ограничить его область как можно больше.

Я не уверен, как часто вещи используют дескриптор файлов три и четыре напрямую - я думаю, что большинство программ времени используют системные вызовы, которые возвращают дескрипторы файлов, не используемых в момент времени, но иногда код записывает в дескриптор файла 3 (я мог бы представить себе программу, проверяющую файловый дескриптор, чтобы увидеть, открыта ли она, и использовать ее, если она есть, или вести себя иначе, если это не так). Поэтому последнее, вероятно, лучше всего учитывать и использовать для случаев общего назначения.

--- НАХОДНЫЙ СОДЕРЖАНИЕ НИЖЕ ЭТОЙ ЛИНИИ ---

По историческим причинам, вот мой исходный, не-портативный для всех shells ответ:

[EDIT] Неплохо, это не работает с bash, потому что bash нуждается в дополнительном усердии при работе с файловыми дескрипторами, я обновлю это, как только смогу. [/EDIT]

Решение Pure Bourne:

exitstatus=`{ 3>&- command1; } 1>&3; printf $?` 3>&1 | command2
# $exitstatus now has command1 exit status.

Это база, на которой вы можете создать свой "eet". Постучите в синтаксическом разборе аргументов командной строки и все это превратите команду2 в "tee" с соответствующими параметрами и т.д.

ОЧЕНЬ подробное объяснение выглядит следующим образом:

На верхнем уровне оператор представляет собой только трубку между двумя командами:

commandA | command2

commandA в свою очередь разбивается на одну команду с перенаправлением дескриптора файла 3 на дескриптор 1 файла (stdout):

commandB 3>&1

Это означает, что оболочка будет ожидать, что commandB что-то напишет в дескриптор файла 3 - если файловый дескриптор 3 никогда не открывается, это будет ошибка. Это также означает, что command2 будет получать любые выходные данные командыB в обоих дескрипторах файлов 1 (stdout) и 3.

commandB, в свою очередь, является присвоением переменной с использованием подстановки команды:

VAR_FOO=`commandC`

Мы знаем, что присваивания переменных ничего не печатают ни в каких дескрипторах файлов (и commandc stdout захвачен для подстановки), поэтому мы знаем, что commandB в целом ничего не выводит на stdout. Таким образом, command2 будет видеть только то, что commandC записывает в дескриптор файла 3.

И commandC - это две команды, где вторая команда печатает статус выхода первого:

commandD ; printf $?

Итак, теперь мы знаем, что присвоение переменной на последнем шаге будет содержать статус выхода commandD.

Теперь командаD распадается на другое базовое перенаправление, в файле comman stdout для дескриптора файла 3:

commandE 1>&3

Итак, теперь мы знаем, что вещь, записывающая в дескриптор файла 3 и, следовательно, в конечном итоге команду command2, является commandE stdout.

Наконец: commandE - это "составная команда" (здесь вы также можете использовать подоболочку, но это не так эффективно), обертывая другой менее широко известный тип "перенаправления":

{ 3>&- command1; }

(Это 3>&- немного сложно, поэтому мы вернемся к нему в конце.) Таким образом, составные команды делают эту точку с запятой обязательной, когда последняя команда и последняя скобка находятся в одной строке, поэтому это там. Поэтому мы знаем, что составные команды возвращают код выхода из их последней команды, и они наследуют файловые дескрипторы, как и все остальное, поэтому теперь мы знаем, что command1 stdout вытекает из составной команды, перенаправляет на дескриптор файла 3, чтобы избежать попадания в подстановку команд, тогда подменю команды улавливает оставшуюся строку вывода printf, которая отгоняет статус выхода команды1 после ее завершения.

И теперь для сложного бита: 3>&- говорит "закрыть дескриптор файла 3". Вы можете подумать: "Почему вы закрываете его, когда вы просто перенаправляете ему вывод команды 1?" Ну, если вы посмотрите внимательно, вы увидите, что эффект close только команды 1 внутри составной команды (внутри фигурных скобок), в то время как перенаправление воздействует на всю составную команду.

Итак, вот что происходит: к моменту запуска отдельных команд составной команды оболочка открыла дескриптор файла 3. Процессы наследуют файловые дескрипторы, поэтому command1 по умолчанию будет запускаться с открытым файловым дескриптором 3 и указывать на такой же место тоже. Это плохо, потому что изредка программы на самом деле ожидают, что конкретные дескрипторы файлов означают особые вещи - они могут вести себя по-разному при запуске с открытым файловым дескриптором 3. Наиболее надежным решением является просто закрыть дескриптор файла 3 (или любой номер, который вы используете) только для команды 1, поэтому он работает так, как если бы файловый дескриптор 3 никогда не был открыт.

Ответ 6

G'day,

Предполагая bash или zsh,

my_command >>my_log 2>&1

N.B. Последовательность перенаправления и дублирования STDERR на STDOUT значительна!

Изменить:. Не понимал, что вы хотите видеть вывод на экране. Это, конечно, направит весь вывод в файл my_log.

НТН

веселит,

Ответ 7

{ mycommand --foo --bar 2>&1; ret=$?; } | tee -a some.log; (exit $ret)

Ответ 8

#!/bin/sh
logfile="$1"
shift
exec 2>&1
exec "[email protected]" | tee "$logfile"

Надеюсь, это сработает для вас.