Как я могу подавить вывод, только если команда выполнена успешно?

22

Я хотел бы упростить вывод сценария, подавив вывод вторичных команд, которые обычно успешны.

Тем не менее, использование -qих скрывает вывод, когда они иногда терпят неудачу, поэтому у меня нет никакого способа понять ошибку. Кроме того, эти команды регистрируют свои выходные данные stderr.

Есть ли способ подавить вывод команды только в случае успеха ?

Например (но не ограничиваясь этим) что-то вроде этого:

mycommand | fingerscrossed

Если все идет хорошо, fingerscrossedловит вывод и сбрасывает его. В противном случае он выводит его на стандартный вывод или вывод ошибки (что угодно).

Матье Наполи
источник

Ответы:

36

moreutils' chronicКоманда делает только что:

chronic mycommand

будет глотать mycommandвывод, если это не сработает, и в этом случае вывод отображается.

Стивен Китт
источник
1
Спасибо. Я так понимаю, он не установлен по умолчанию на большинстве ОС Unix?
Матье Наполи
1
Вероятно, нет, хотя он широко упакован, поэтому его легко установить.
Стивен Китт
1
У Debian это есть в упаковке moreutils. Во всяком случае,
приятно
6
Обратите внимание, что он хранит весь вывод в памяти.
Стефан Шазелас
1
@ StéphaneChazelas, это, вероятно, единственный способ реализовать что-то вроде этого, вывод должен быть сохранен во время выполнения команды, если это необходимо.
Сентиман
11
### do this bit once at the top of your script
divert=
exec 3<>"${divert:=$(mktmp)}" 4<>/dev/null
rm -- "$divert"; unset divert
### then do this bit as often as needed
command >&3 2>&3
cat <&3 >&"$(((RTN=$?)?2:4))"

Это, вероятно, должно сработать. Он буферизует вывод каждого из них commandв удаленный временный файл, а затем откачивает свой вывод в любой из них /dev/nullили в stderr в зависимости от того, был ли его статус возврата не нулевым. Поскольку временный файл удаляется досрочно, он не может быть прочитан каким-либо процессом, кроме текущей оболочки и ее дочерних элементов в дескрипторе файла (за исключением скрытого /proc/$pid/fdотслеживания с соответствующими разрешениями) , и он не требует очистки по окончании.

Возможно, более удобное решение для систем Linux:

divert(){
    "$@" >&3 2>&3 ||
    eval "cat <&3
          return $?"
}   3<<"" 3<>/dev/fd/3

... которые, в большинстве оболочек, так же, как другие работают, за исключение того, что вы можете назвать это как: divert some simple-command with args. Остерегайтесь высокопроизводительных команд в "$@", хотя для dash, yashили в некоторых других оболочках, которые делают здесь - документы с каналами - я думаю, что в этих оболочках может быть возможно заполнить буфер канала (по умолчанию около 128 КБ в linux) и таким образом тупик , Это не должно быть беспокойства для ksh, mksh, bash, zsh, или Bourne оболочки, хотя - все те делают в основном то же самое, что я явно выше exec.

mikeserv
источник
9

Обычно в случае ошибки команда выводит сообщения, stderrпоэтому для вашей задачи вы можете просто подавитьstdout

mycommand > /dev/null
Костас
источник
Спасибо, но, как я уже сказал в этом вопросе, мои команды регистрируют все выходные данные stderr(так что это не имеет никакого эффекта).
Матье Наполи
4

Сделать свой хронический

my_chronic() {
  tmp=$(mktemp) || return # this will be the temp file w/ the output
  "$@"  > "$tmp" 2>&1 # this should run the command, respecting all arguments
  ret=$?
  [ "$ret" -eq 0 ] || cat "$tmp"  # if $? (the return of the last run command) is not zero, cat the temp file
  rm -f "$tmp"
  return "$ret" # return the exit status of the command
}
Джейкоб Миншалл
источник
3

Я делаю что-то подобное в моих make-файлах:

if (mycommand) &> mycommand.log; then 
  echo success 
else 
  c=$?; 
  echo;echo -e "Bad result from previous command, see mycommand.log for more details";echo;
  command_to_run_on_fail
  (exit $c)
fi

Адаптируя это к вашей ситуации, вы можете сделать что-то вроде этого:

if ! (mycommand) &> mycommand.log; then 
  c=$?; 
  cat mycommand.log
  rm mycommand.log
  (exit $c)
fi

Таким образом, «if» запускает команду и направляет вывод в mycommand.log. Если вам нужно поймать stdout vs stdout vs что угодно, вам может потребоваться изменить команду конвейера '&>' на '>'. Если команда не выполнена, запишите код ошибки, распечатайте содержимое файла mycommand.log, удалите файл mycommand.log и, наконец, верните исходный код ошибки.

Без (exit $ c) вы вернетесь с кодом выхода, который соответствует тому, что вернула команда 'rm'.

Наконец, если вы хотите один лайнер, что-то вроде этого будет работать.

mycommand &> mycommand.log || cat mycommand.log; rm mycommand.log
Иордания
источник
2
Вы действительно оборачиваете свои команды / и т.д. в (...)этом? Потому что это не делает ничего полезного для вас, но порождает дополнительные вложенные оболочки.
Этан Рейснер
@EtanReisner (exit $c)- это настройка $?, которую вы не можете сделать иначе. if ! (mycommand) &>xимеет смысл с перенаправлением, если команда использует, например, timeили выдает ошибку оболочки.
Майкл Гомер
@MichaelHomer - для этих вещей есть { ; }завитки ... хотя exitэто немного сложно, по общему признанию.
mikeserv
В фрагменте make-файла, если вы пытаетесь выйти с ранее сохраненным, $?вы можете просто использовать, exit $cно да, в других случаях (exit $?)имеет значение (хотя в целом функция оболочки rret() { return $1; }была бы лучше, я бы поспорил). Тем не менее, как показывает mikeserv, подоболочка для команд все еще не работает.
Этан Рейснер
3

Я просто нашел гораздо более простой ответ на этот другой вопрос :

output=`mycommand 2>&1` || echo $output

Работает как шарм!

Матье Наполи
источник
Примечание: для моего варианта использования это было гораздо более простое решение (не устанавливайте дополнительные компоненты на всех CI-серверах), поэтому я решил пометить это как принятое. YMMV.
Матье Наполи
Обратите внимание, что если вы используете set -o xtrace в своем сценарии оболочки, тогда все выходные данные будут там снова, как часть регистрации подробностей выходных данных присваивания = ... :-). В этом случае, вероятно, лучше использовать хронический.
Ян-Филипп Герке