Как совместить группировку команд и состояние канала

Как объединить группировку команд bash и статус трубы?

Это примерная группа команд:

{ tar -cf - my_folder 2>&1 1>&3 | grep -v "Removing leading" 1>&2; } 3>&1 | gzip --rsyncable > my_file.tar.gz 

Это пример показания состояния трубопровода, чтобы перейти к следующему:

 [[ ${PIPESTATUS[*]} =~ [1-9] ]] && rm my_file.tar.gz 

В этом примере группа команд хранит почтовую катушку без предупреждений о «удалении ведущего /» из tar, доставляемого через cron, потому что они приземляются на stderr (Unices не хватает stdwarn и tar без тихой опции), позволяя реальным ошибкам проходить через ,

Считывание состояния трубы гарантирует, что поврежденные файлы резервных копий будут немедленно удалены, чтобы предотвратить последующую очистку с использованием стандартного алгоритма FIFO для удаления старых файлов.

Но этот пример не работает. В приведенном выше состоянии состояние канала содержит [1 0], то есть код выхода grep и gzip, но не tar.

Одна попытка, которую я пробовал, состояла в следующем:

 { tar -cf - my_folder 2>&1 1>&3 | grep -v "Removing leading" 1>&2; GROUPSTATUS=${PIPESTATUS[*]}; } 3>&1 | gzip --rsyncable > my_file.tar.gz [[ $GROUPSTATUS =~ [1-9] ]] && rm my_file.tar.gz 

Но GROUPSTATUS пуст после выхода из группы.

(Заметим, что, установив GROUPSTATUS на что-либо, кроме состояния канала, например, на литерал какого-либо типа, можно проверить, что эта переменная фактически исключает область группировки команд при обычных обстоятельствах.)

Я также пробовал, если return изнутри группы может привести к выходу кода выхода первого компонента трубы наружу, но возврат внутри командной группы просто приводит к сообщению об ошибке из bash.

2 Solutions collect form web for “Как совместить группировку команд и состояние канала”

Когда вы выполняете конвейер, каждый элемент, разделенный на трубы, выполняется в своем собственном процессе. Переменные назначения вступают в силу только в их собственном процессе. В ksh и zsh последний элемент конвейера выполняется в исходной оболочке; под другими оболочками, такими как bash, каждый элемент конвейера выполняется в своей собственной подоболочке, а исходная оболочка просто ждет их завершения.

 $ bash -c 'GROUPSTATUS=foo; echo GROUPSTATUS is $GROUPSTATUS' GROUPSTATUS is foo $ bash -c 'GROUPSTATUS=foo | :; echo GROUPSTATUS is $GROUPSTATUS' GROUPSTATUS is 

В вашем случае, поскольку вы заботитесь только обо всех командах, вы можете сделать код состояния текущим.

 { tar -cf - my_folder 2>&1 1>&3 | grep -v "Removing leading" 1>&2; ! ((PIPESTATUS[0])); } 3>&1 | gzip --rsyncable > my_file.tar.gz; if ((PIPESTATUS[0] || PIPESTATUS[1])); then rm my_file.tar.gz; fi 

Если вы хотите получить более 8 бит информации из левой части канала, вы можете записать еще один дескриптор файла. Вот пример доказательства:

 { { tar …; echo $? >&4; } | …; } | { gzip …; echo $? >&4; } \ 4>&1 | ! grep -vxc '0' 

Как только вы получите данные на стандартном выходе, вы можете передать его в переменную оболочки с помощью подстановки команд, то есть $(…) . Подстановка команд считывается из стандартного вывода команды, поэтому, если вы также должны печатать на стандартном выходе сценария, им необходимо временно перейти через другой дескриптор файла. Следующий фрагмент использует fd 3 для вещей, которые в конечном итоге переходят к скрипту stdout и fd 4 для вещей, которые захватываются в $statuses .

 statuses=$({ { tar -v … >&3; echo tar $? >&4; } | …; } | { gzip …; echo gzip $? >&4; } 4>&1) 3>&1 

Если вам нужно записать вывод из разных команд в разные переменные, я думаю, что нет прямого пути даже в «продвинутых» оболочках, таких как bash, ksh или zsh. Вот некоторые обходные пути:

  • Используйте временные файлы.
  • Используйте один выходной поток, например, префикс в каждой строке, чтобы указать его начало, и фильтр на верхнем уровне.
  • Используйте более продвинутый язык, такой как Perl или Python.

Простым способом является обход проблемы, заключается в том, чтобы поместить my_folder в переменную оболочки и удалить из нее главную косую черту:

 tar -cf - ${my_folder##/} | gzip --rsyncable > my_file.tar.gz 

В противном случае вы можете проверить $ {PIPESTATUS [0]} внутри блока и использовать false, если он не равен нулю, чтобы сигнализировать об ошибке внешнему процессу:

 { tar -cf - my_folder 2>&1 1>&3 | grep -v "Removing leading" 1>&2; [ ${PIPESTATUS[0]} -eq 0 ] && true || false; } 3>&1 | gzip --rsyncable > my_file.tar.gz 
  • Тест регулярного выражения Bash не работает
  • Почему $ '\ 0' совпадает с ''?
  • Как я могу предотвратить выполнение части / etc / profile в моей оболочке входа?
  • Почему в ssh удален вызов?
  • Создание интерактивной оболочки для команды
  • Имена файлов, содержащие пробелы, указаны неправильно.
  • Функция «pathmunge» для произвольных «PATH» -подобных переменных
  • добавление специальных символов в имена папок
  • Нет цветного вывода с параметром --login в bash
  • Ничего не делать в сценарии bash
  • скажите, была ли последняя команда пуста в PROMPT_COMMAND
  • Как получить новую строку перед вызовом bash?
  • Linux и Unix - лучшая ОС в мире.