Bash: Назначение переменной не похоже на «stick»

Мне сложно отслеживать причину, по которой моя переменная флага «boolean» не останется ложной, когда тест завершится неудачно внутри цикла while.

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

Чтобы сделать это, я прочитал файл wakeUp_blacklist.txt с именами альбомов, которые я не хотел слышать, чтобы он мог отклонить любые совпадения. Это сравнивает альбомы с черным списком по очереди с одним случайно выбранным альбомом и тестами для матча. Если совпадение найдено, оно помечает совпадение как сбой ( pass = false ), а SHOULD – назад и выбирает другой альбом. По какой-то причине, как только выполнение завершает read | while read | while loop, флаг переключается на true, и поэтому другой альбом никогда не выбирается!

Вот мой код и пример вывода:

 #!/bin/bash wd="/media/External1/albums" pass="false" for ((i=0; i<=1; i++)); do while [ "$pass" == "false" ]; do album=`ls -d "$wd"/*/*/ | sort -R | tail -n 1` echo "album selected:" echo "$album" pass="true" echo "pass reset; pass=$pass" cat "$wd/wakeUp_blacklist.txt" | while read black; do echo "pass check 2:$pass" echo "black: $wd/$black" if [ "$album" == "$wd/$black" ] || [ "$album\n" == "$albums" ]; then pass="false" echo "test failed; pass=$pass" fi echo "pass check 1:$pass" done echo "Blacklist check complete; pass=$pass" #Why is it true again?! done pass="false" albums="$albums$album\n" echo "album assigned:" echo "$album" done echo echo for album in "$albums"; do # play "$album*" echo -e "$album" #List album names rather than actually play them done 

Итак, если я запустил это несколько раз, он в конечном итоге выберет один из альбомов с черным списком и выдаст результат, который выглядит так:

 album selected: /media/External1/albums/Adele/21/ pass reset; pass=true pass check 2:true black: /media/External1/albums/Vince_Guaraldi_Trio/A_Charlie_Brown_Christmas/ pass check 1:true pass check 2:true black: /media/External1/albums/Sound_Inventions/Stille_Nacht-_A_German_Christmas/ pass check 1:true Blacklist check complete; pass=true album assigned: /media/External1/albums/Adele/21/ album selected: /media/External1/albums/Sound_Inventions/Stille_Nacht-_A_German_Christmas/ pass reset; pass=true pass check 2:true black: /media/External1/albums/Vince_Guaraldi_Trio/A_Charlie_Brown_Christmas/ pass check 1:true pass check 2:true black: /media/External1/albums/Sound_Inventions/Stille_Nacht-_A_German_Christmas/ test failed; pass=false pass check 1:false Blacklist check complete; pass=true album assigned: /media/External1/albums/Sound_Inventions/Stille_Nacht-_A_German_Christmas/ /media/External1/albums/Adele/21/ /media/External1/albums/Sound_Inventions/Stille_Nacht-_A_German_Christmas/ 

Вы можете видеть, что в конце, когда запись черного списка соответствует выбранному альбому (German_Christmas), тест терпит неудачу, переменная pass имеет значение false но первый вывод, который проверяет значение pass за пределами read | while read | while цикл показывает, что это правда снова!

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

One Solution collect form web for “Bash: Назначение переменной не похоже на «stick»”

Вы делаете:

 cat "$wd/wakeUp_blacklist.txt" | while read black; do 

В Баше :

Каждая команда в конвейере выполняется в своей собственной подоболочке

Подселлем является еще один пример bash с собственным состоянием. Это означает, что любая переменная, которую вы изменяете в правой части | концептуально «принадлежат» к другому экземпляру bash – копируются существующие значения и декларации в начале, но переменные имеют разные места хранения, а модификации для них видны только внутри этой подоболочки (вы можете думать об этом как о работе как fork , если хочешь).

Когда подоболочка завершается, эта копия переменной перестает существовать. «Внешняя» оболочка только когда-либо видит свою собственную, немодифицированную копию этой переменной, поэтому представляется, что вы теряете свои модификации вне цикла.


В этом случае вы можете избежать использования подоболочки с простым перенаправлением :

  while read black; do ... pass="false" ... done < "$wd/wakeUp_blacklist.txt" 

Теперь цикл while работает в вашей основной оболочке, и не создается подоболочка. Содержимое черного списка по-прежнему предоставляется в качестве стандартного ввода в цикл с использованием < file перенаправления < file . Все переменные существуют внутри одной и той же среды. Этот подход также позволяет избежать бесполезного использования cat .

Некоторые другие оболочки (особенно zsh ) не имеют такого поведения для начала, и вы можете свободно изменять переменные в правой части конвейеров.

  • Как я могу объединить переменную оболочки с другими другими параметрами в моих командных строках?
  • dirname и basename против расширения параметров
  • Объединение нескольких выходов grep в переменную
  • Как выбрать количество цифр в переменной в моих сценариях?
  • Присвоение нового значения непосредственно в индекс символа значения в массиве с zsh
  • bash - определяющие переменные с VAR = $ {: - по умолчанию}
  • команда `read` не работает в Makefile
  • захватить из текстового файла диапазон с использованием двух переменных в качестве начального и конечного параметров
  • Разделить команду и аргументы и вставить в переменную в сценарий bash
  • bash - добавить пустую строку в heredoc через переменную
  • Развертывание Jenkins не передается переменной java
  • Linux и Unix - лучшая ОС в мире.