Обновить переменную значением из цикла while

#bash

#bash

Вопрос:

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

 #!/bin/bash

function Upload {
  find . -type f -name "*.test" -printf "%fn"| sort -k1 | while read fname; do
  progressBar=$(echo "scale=2 ; $progress   $percentage" | bc)
  echo "bar: $progressBar"
  progress=$progressBar
done
}

progress=0
totalFiles=$(find . -name "*.test" | wc -l)
totalCalc=$(($totalFiles   1))
percentage=$(echo "scale=2 ; 100 / $totalCalc" | bc)

Upload

echo $progress
 

Как я могу получить переменную вне цикла / подоболочки и перезаписать основную переменную?

Комментарии:

1. Помимо решения, изложенного в моем ответе, вы также можете написать свою программу, скажем, на Perl, Python или Ruby, где у вас есть все в одном процессе. progressBar Тогда ваша будет просто глобальной переменной.

Ответ №1:

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

Запись в файл is и извлечение его в вашей «основной» программе легко, если вы делаете это правильно:

В вашей основной программе (перед вызовом вашей функции) настройте файл следующим образом:

 export progressBarFile=/tmp/progressBar.$
 

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

В вашем цикле выполните

 echo $progressBar >$progressBarFile
 

Затем, после выполнения вашей функции, извлеките значение с помощью

 progressBar=$(<$progressBarFile)
 

В конце концов, вы можете удалить этот файл следующим образом:

 rm $progressBarFile
 

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

Другой возможностью (без использования файла) было бы использование массива:

 files=( $(find . -type f -name "*.test" -printf "%fn"| sort -k1) )
for fname in "${files[@]}"
do
   ...
done
 

В этом случае тело вашего цикла не является дочерним процессом.

Комментарии:

1. Спасибо за объяснение. Причина, по которой я хочу избежать записи во внешний файл, заключается в том, что в реальном скрипте я записываю прогресс в базу данных mysql, чтобы моя веб-страница могла считывать прогресс из нее. Я подумал, что если это невозможно сделать без сохранения переменной вне скрипта, я мог бы также извлечь ее из своей базы данных. Я не программист и не учусь, пока работаю над этим, поэтому я предпочитаю не добавлять в микс другой язык программирования.

2. @sjaak: Так, может быть, вы можете использовать мое второе предложение (используя массив, чтобы избежать дочернего процесса)?