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 ) не имеют такого поведения для начала, и вы можете свободно изменять переменные в правой части конвейеров.

  • Как использовать расширенное значение переменной оболочки в имени другой переменной?
  • Область переменных в while-read-loop на Solaris
  • Переменная в команде find задана новой переменной в bash
  • Как сохранить статус последнего выхода после теста
  • Использование «$ {a: -b}» для назначения переменных в скриптах
  • В сценарии bash, что отличается между объявлением и нормальной переменной?
  • имя сценария скрипта echo text name для циклизации нескольких текстов
  • Что означает «$ {x %% *}» в sh?
  • dirname и basename против расширения параметров
  • Объявление переменной параллельным sh -c ...
  • захватить из текстового файла диапазон с использованием двух переменных в качестве начального и конечного параметров
  • Невозможно назначить вывод вложенных команд переменной в bash
  • Linux и Unix - лучшая ОС в мире.