Дан фрагмент Bash-скрипта: cat file | while read line; do arr+=("$line"); done; echo "${#arr[@]}". Объясните, почему счётчик элементов может быть нулевым после цикла в некоторых средах, и как правильно переписать скрипт, чтобы избежать этой проблемы
Коротко — потому что в многих оболочках команда справа от пайпа выполняется в подсhell`e`, и изменения массива там теряются при возврате в родительский шелл. В результате после `cat file | while read line; do arr+=("$line"); done` в основном процессе массива может не быть и счётчик будет равен 000. Правильные варианты исправления (коротко, портативно и безопасно): 1) Перенаправление ввода в цикл (рекомендуемо): ``` while IFS= read -r line; do arr+=("$line") done < file echo "${#arr[@]}" ``` Пояснения: `IFS=` сохраняет пробелы/табуляции, `-r` отключает интерпретацию обратных слешей. Цикл выполняется в том же процессе, массив сохраняется. 2) Использовать readarray/mapfile (bash): ``` mapfile -t arr < file echo "${#arr[@]}" ``` Быстро и удобно; `-t` убирает завершающий символ новой строки. 3) (Менее портативно) включить в bash опцию lastpipe в неинтерактивной сессии: ``` shopt -s lastpipe cat file | while IFS= read -r line; do arr+=("$line"); done echo "${#arr[@]}" ``` Работает только в bash (4.2+) и при выключенном управлении заданиями. Вывод: используйте перенаправление (`done < file`) или `mapfile` — тогда счётчик элементов не будет равен 000.
Правильные варианты исправления (коротко, портативно и безопасно):
1) Перенаправление ввода в цикл (рекомендуемо):
```
while IFS= read -r line; do
arr+=("$line")
done < file
echo "${#arr[@]}"
```
Пояснения: `IFS=` сохраняет пробелы/табуляции, `-r` отключает интерпретацию обратных слешей. Цикл выполняется в том же процессе, массив сохраняется.
2) Использовать readarray/mapfile (bash):
```
mapfile -t arr < file
echo "${#arr[@]}"
```
Быстро и удобно; `-t` убирает завершающий символ новой строки.
3) (Менее портативно) включить в bash опцию lastpipe в неинтерактивной сессии:
```
shopt -s lastpipe
cat file | while IFS= read -r line; do arr+=("$line"); done
echo "${#arr[@]}"
```
Работает только в bash (4.2+) и при выключенном управлении заданиями.
Вывод: используйте перенаправление (`done < file`) или `mapfile` — тогда счётчик элементов не будет равен 000.