Як я можу придушити вихід, лише якщо команда успішна?


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або деякі інші оболонки , які роблять тут-документи з трубами - Я думаю , що це може бути можливо в цих оболонках , щоб заповнити буфер труби (при дефолті близько 128kb на дистрибутивів Linux) і так тупику . Це не повинно бути занепокоєння для ksh, mksh, bash, zsh, або Bourne оболонки, хоча - все ті роблять в основному те ж саме, що я явно вище exec.


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

Я роблю щось подібне у своїх грифах:

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 на будь-який інший, вам може знадобитися змінити команду pipe '&>' на '>'. Якщо команда не вдається, тоді зафіксуйте код помилки, роздрукуйте вміст 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

У фрагменті makefile, якщо ви намагаєтеся вийти із збереженим раніше, $?тоді ви можете просто використовувати, exit $cале так, в інших випадках (exit $?)має значення (хоча функція оболонки rret() { return $1; }, як правило, краще, я б заперечував). Підрозділ для команд все ще насправді не так, як показав mikeserv.
Ітан Рейснер

3

Щойно я знайшов набагато простішу відповідь на це інше питання :

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

Працює як шарм!


Примітка: для мого випадку це було набагато простішим рішенням (уникайте встановлення зайвих матеріалів на всіх серверах CI), тому я вирішив позначити цей як прийнятий. YMMV.
Матьє Наполі

Зауважте, що якщо ви використовуєте set -o xtrace у вашому скрипті оболонки, то весь результат знову буде там, як частина реєстрації реквізитів виводу призначення = ... :-). У такому випадку, мабуть, краще використовувати хронічний.
Ян-Філіп Геррк
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.