Як спрямувати вхід до циклу Bash while та зберегти змінні після закінчення циклу


87

Bash дозволяє використовувати: cat <(echo "$FILECONTENT")

Bash також дозволяють використовувати: while read i; do echo $i; done </etc/passwd

для поєднання попередніх двох це можна використовувати: echo $FILECONTENT | while read i; do echo $i; done

Проблема останнього полягає в тому, що він створює допоміжну оболонку, і після закінчення циклу while змінна iбільше не доступна.

Моє запитання:

Як досягти чогось такого: while read i; do echo $i; done <(echo "$FILECONTENT")або іншими словами: Як я можу бути впевненим, що iвиживає цикл while?

Зверніть увагу, що мені відомо, що я вкладаю оператор while, {}але це не вирішує проблему (уявіть, що ви хочете використовувати цикл while у функції і iзмінної return )



Пов’язані: stackoverflow.com/questions/37229058/… . Пояснює всі варіанти, включаючи зазначену нижче заміну процесу, а lastpipeтакож їх плюси та мінуси.
ivan_pozdeev

Відповіді:


115

Правильним позначенням для заміщення процесу є:

while read i; do echo $i; done < <(echo "$FILECONTENT")

Тоді останнє значення, iпризначене в циклі, стає доступним, коли цикл завершується. Альтернативою є:

echo $FILECONTENT | 
{
while read i; do echo $i; done
...do other things using $i here...
}

Фігурні дужки - це операція групування вводу-виводу, яка сама по собі не створює під оболонку. У цьому контексті вони є частиною конвеєра і, отже, працюють як допоміжна оболонка, але це відбувається через |, а не { ... }. Ви згадуєте це у питанні. AFAIK, ти можеш зробити повернення зсередини всередині функції.


Bash також надає shoptвбудовану програму, і одним із багатьох варіантів є:

lastpipe

Якщо встановлено, а керування завданнями не активне, оболонка запускає останню команду конвеєра, який не виконується у фоновому режимі в поточному середовищі оболонки.

Таким чином, використання подібного у сценарії робить модифіковану sumдоступною після циклу:

FILECONTENT="12 Name
13 Number
14 Information"
shopt -s lastpipe   # Comment this out to see the alternative behaviour
sum=0
echo "$FILECONTENT" |
while read number name; do ((sum+=$number)); done
echo $sum

Роблячи це в командному рядку, зазвичай виникає помилка "контроль роботи неактивний" (тобто в командному рядку контроль роботи активний). Не вдалося перевірити це без використання сценарію.

Крім того, як зазначив Гарет Різ у своїй відповіді , іноді ви можете використовувати рядок тут :

while read i; do echo $i; done <<< "$FILECONTENT"

Для цього не потрібно shopt; можливо, ви зможете зберегти процес, використовуючи його.


Вибачте моє незнання. Я знаю, що це правильне рішення, і я позначив його як відповідь, тому воно спрацювало для мене. Але тепер, коли я запускаю, while read i; do echo $i; done < <(cat /etc/passwd); echo $iвін не повертався останній рядок два рази. Що я роблю не так?
Вакан Танка

@WakanTanka: Мені довелося трохи поекспериментувати ... Я вважаю, що відповідь полягає в тому, що збій читання скидає iна порожнє, тому луна після циклу повторює порожній рядок.
Джонатан Леффлер

1
Похвала за посилання на заміну процесу. Я не знав про це.
Atcold

@Wakan Tanka: отримав такий самий результат, як і ваш, я використовую while read i; do x=$i; done < <(cat /etc/passwd); echo i=$i; echo x=$xпрацював, // можливо, ті дні поведінка bash змінилася?
yurenchen

Ви рятуєте життя! Шукав години подібного рішення. Твій єдиний працював.
патент


0

Ця функція робить копії файлів JPG у розмірі $ NUM (bash)

function makeDups() {
NUM=$1
echo "Making $1 duplicates for $(ls -1 *.jpg|wc -l) files"
ls -1 *.jpg|sort|while read f
do
  COUNT=0
  while [ "$COUNT" -le "$NUM" ]
  do
    cp $f ${f//sm/${COUNT}sm}
    ((COUNT++))
  done
done
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.