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

Как объединить группировку команд 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.

  • Запишите любую команду, похожую на `time`
  • Сортировка файлов с использованием сценария bash
  • Терминал говорит, что команда не найдена при запуске
  • Как пропустить подкаталоги в цикле for, основанном только на имени?
  • Преобразование всех первых символов в идентификатор электронной почты в верхний регистр в BASH
  • Bash цитирует расширение
  • Разве два стиля for-loops отличаются тем, что они могут выразить?
  • разница между `for i в {1 .. $ N}` и `для i в $ (seq 1 1 $ N)`
  • 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 
    Linux и Unix - лучшая ОС в мире.