Как правильно удалить код выхода / обработать ошибки при использовании замещения процесса?

У меня есть скрипт, который анализирует имена файлов в массиве, используя следующий метод, взятый из Q & A на SO :

unset ARGS ARGID="1" while IFS= read -r -d $'\0' FILE; do ARGS[ARGID++]="$FILE" done < <(find "$@" -type f -name '*.txt' -print0) 

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

 $ findscript.sh existingfolder nonexistingfolder find: `nonexistingfile': No such file or directory ... 

В обычных условиях я бы скрипт захватил код выхода с чем-то вроде RET=$? и использовать его, чтобы решить, как действовать. Это, похоже, не работает с замещением процесса выше.

Какова правильная процедура в таких случаях? Как я могу захватить код возврата? Существуют ли другие более подходящие способы определения того, что что-то пошло не так в замещенном процессе?

  • Остановить выполнение, когда что-то написано в stderr
  • Почему «синтаксическая ошибка около неожиданного токена»?
  • Линейный выход литиевого выхода
  • Как вызвать ошибку с помощью команды Trap
  • Ошибка: «TCP_NODELAY» не был объявлен в этой области
  • TCL скрипт try catch
  • open () вернуть новый дескриптор файла posix
  • Как проверить, завершена ли команда, такая как завиток, без ошибок?
  • 4 Solutions collect form web for “Как правильно удалить код выхода / обработать ошибки при использовании замещения процесса?”

    Вы можете довольно легко получить возврат от любого подпольного процесса, повторив его возврат на его stdout. То же самое относится к замещению процесса:

     while IFS= read -r -d $'\0' FILE || ! return=$FILE do ARGS[ARGID++]="$FILE" done < <(find . -type f -print0; printf "$?") 

    Если я запустил это, то самая последняя строка – (или раздел с разделителем \0 в зависимости от случая) будет возвращать статус возврата. read вернет 1, когда получит EOF – поэтому единственный раз, когда $return установлен в $FILE – это самый последний бит информации, который читается.

    Я использую printf чтобы не добавлять дополнительный \n ewline – это важно, потому что даже read выполняемое регулярно – то, которое вы не разделяете на \0 NUL, будет возвращать, кроме 0, в тех случаях, когда данные, которые у него есть чтение не заканчивается на \n ewline. Поэтому, если ваша последняя строка не заканчивается символом \n ewline, последнее значение в вашей переменной чтения будет вашим возвратом.

    Выполняя команду выше, а затем:

     echo "$return" 

    ВЫВОД

     0 

    И если я изменю часть замещения процесса …

     ... done < <(! find . -type f -print0; printf "$?") echo "$return" 

    ВЫВОД

     1 

    Более простая демонстрация:

     printf \\n%s list of lines printed to pipe | while read v || ! echo "$v" do :; done 

    ВЫВОД

     pipe 

    И на самом деле, пока требуемое возвращение – это последнее, что вы пишете на stdout из подстановки процесса – или любого подчиненного процесса, из которого вы читаете таким образом, – тогда $FILE всегда будет статусом возврата, который вы хотите когда это пройдет. И так || ! return=... || ! return=... || ! return=... часть не является строго необходимой – она ​​используется для демонстрации только концепции.

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

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

     … < <(find …; echo $? >find.status.tmp; mv find.status.tmp find.status) while ! [ -e find.status ]; do sleep 1; done find_status=$(cat find.status; rm find.status) 

    Другой подход – использовать именованный канал и фоновый процесс (который вы можете wait ).

     mkfifo find_pipe find … >find_pipe & find_pid=$! … <find_pipe wait $find_pid find_status=$? 

    Если ни один из подходов не подходит, я думаю, вам нужно перейти на более удобный язык, такой как Perl, Python или Ruby.

    Один из подходов:

     status=0 token="WzNZY3CjqF3qkasn" # some random string while read line; do if [[ "$line" =~ $token:([[:digit:]]+) ]]; then status="${BASH_REMATCH[1]}" else echo "$line" fi done < <(command; echo "$token:$?") echo "Return code: $status" 

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

    Вероятно, это не лучший способ сделать это в общем смысле программирования, но это может быть наименее болезненный способ справиться с этим в bash.

    Используйте сопроцесс . Используя встроенный coproc вы можете запустить подпроцесс, прочитать его вывод и проверить его статус выхода:

     coproc LS { ls existingdir; } LS_PID_=$LS_PID while IFS= read i; do echo "$i"; done <&"$LS" wait "$LS_PID_"; echo $? 

    Если каталог не существует, wait выйдет с ненулевым кодом состояния.

    В настоящее время необходимо скопировать PID на другую переменную, потому что $LS_PID будет $LS_PID до wait . См. Bash unsets * _PID переменную, прежде чем я могу подождать на coproc для получения дополнительной информации.

    Interesting Posts

    Подключение 3G-модема работает только после Windows

    Изменение метки времени символической ссылки

    Сценарий Bash порождает множество новых процессов сам по себе

    Сделайте tar (или другой) архив с выравниванием блоков данных, как в исходных файлах, для лучшей дедупликации на уровне блока?

    Как отключить экран GNU от внесения изменений в заголовок окна шпатлевки

    Понимание сценария сигнала

    Выполнять команды из истории, которые соответствуют регулярному выражению или grep

    Emacs ansi-term с zsh: ошибка в технологическом фильтре. Недопустимое лицо: неуказано

    Слишком много открытых портов в Debian 8

    Не удается прослушивать эфемерный порт tcp

    Настройка значка заголовка Kde, добавление значка изменения размера

    Моя Bluetooth-гарнитура работает, и Linphone работает, но не вместе

    Как загрузить файл путем подделки IP-адреса запроса?

    Удалить защиту от записи на флэш-накопителе USB

    SFTP: предоставить пользователю доступ к папке вне дома

    Linux и Unix - лучшая ОС в мире.