Bash: Объем переменных в цикле for с использованием тройника

Рассматривать:

numbers="1 111 5 23 56 211 63" max=0 for num in ${numbers[@]}; do [ $num -gt $max ]\ && echo "old -> new max = $max -> $num"\ && max=$num done | tee logfile echo "Max= $max" 

Если я удалю | tee logfile | tee logfile максимальная переменная правильно напечатана как 211, но если я ее оставлю, я получу Max= 0 .

Что происходит?

3 Solutions collect form web for “Bash: Объем переменных в цикле for с использованием тройника”

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

В bash вместо использования трубы вы можете использовать замену процесса . Это будет вестись почти одинаково с трубой, за исключением того, что цикл выполняется в исходной оболочке, и только tee выполняется в подоболочке.

 for num in "${numbers[@]}"; do if ((num > max)); then echo "old -> new max = $max -> $num" max=$num fi done > >(tee logfile) echo "Max= $max" 

Пока я был в этом, я изменил несколько вещей в вашем скрипте:

  • Всегда используйте двойные кавычки вокруг переменных замещений, если вы не знаете, почему их нужно оставить.
  • Не используйте && если вы имеете в виду, if . Это менее читаемо и не имеет такого же поведения, если используется статус возврата команды.
  • Поскольку я использую конструкторы, специфичные для bash, я мог бы также использовать арифметический синтаксис .

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

Другой подход – использовать другой канал для связи от подоболочки к исходному процессу оболочки. Вы можете использовать временный файл (гибкий, но сложнее сделать правильно – записать временный файл в соответствующий каталог, избежать конфликтов имен (используйте mktemp ), удалить его даже в случае ошибок). Или вы можете использовать другой канал для передачи результата и захвата результата в подстановке команд.

 max=$({ { for … done; echo "$max" >&4 } | tee logfile >&3; } 4>&1) 3&>1 

Выход tee переходит к файловому дескриптору 3, который перенаправляется на стандартный вывод сценария. Вывод echo "$max" относится к файловому дескриптору 4, который перенаправляется в подстановку команд.

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

Он не переживает трубы, но это работает:

 numbers="1 111 5 23 56 211 63" max=0 echo $max>maxfile for num in ${numbers[@]}; do [ $num -gt $max ]\ && echo "old -> new max = $max -> $num"\ && max=$num\ && echo $max>maxfile done | tee logfile read max<maxfile echo "Max= $max" 
  • Поведение задания массива
  • Использование «$ {a: -b}» для назначения переменных в скриптах
  • Несколько входов в одной команде
  • шаблон awk с переменной в скрипте bash
  • Что означает «$ {x %% *}» в sh?
  • Как я могу объединить переменную оболочки с другими другими параметрами в моих командных строках?
  • Может ли awk использовать идентификаторы полей также для строк оболочки (переменных)?
  • Переменные баша в команде
  • Установка переменной, значение которой зависит от другой переменной
  • В сценарии bash, что отличается между объявлением и нормальной переменной?
  • захватить из текстового файла диапазон с использованием двух переменных в качестве начального и конечного параметров
  • Linux и Unix - лучшая ОС в мире.