Проблемы с перебором нескольких массивов Bash в одном цикле

Была такая же проблема при выполнении Джессиapt upgrade(при подготовке к удаленному -обновлению ).

Решение было просто

apt-get remove db5.1-util
5
17.03.2021, 12:49
2 ответа

Ваш цикл фактически зацикливается на первых элементах Aи B, то есть ${A[0]}и ${B[0]}, что позже означает, что ${n[1]}и ${n[2]}будут пустыми строками, а ${n[0]}— это то же, что $n.

Я бы, вероятно, использовал переменную ссылки на имя, чтобы сделать это вbash(версии 4.3 или более поздней):

#!/bin/bash

A=( 127.0.0.1 localhost alpha beta gamma theta )
B=( 8.8.8.8   dns       itsy bitsy 'spider man' )

for variable in A B; do
        declare -n array="$variable"

        if ping -c3 "${array[0]}" >/dev/null; then
                printf '%s\n' "${array[1]}"
                printf '\t%s\n' "${array[@]:2}"
        fi
done

Я перебираю имена переменных Aи B, а не их содержимое. В цикле я создаю arrayкак ссылку имени на переменную с именем $variable. Теперь arrayиспользуется точно так же, как Aили B.

В операторе ifя не полагаюсь на то, что оболочка разбивает третий элемент массива для зацикливания. Вместо этого я разделяю его вручную при создании массивов Aи Bи просто вывожу эти элементы одним вызовом printf, используя смещение в массиве array. Я вывожу вкладку перед каждым элементом для удобства чтения.

localhost
        alpha
        beta
        gamma
        theta
dns
        itsy
        bitsy
        spider man

Оператор ifможно также записать как

if ping -c3 "${array[0]}" >/dev/null; then
        printf '%s: ' "${array[1]}"
        ( IFS=,; printf '%s\n' "${array[*]:2}" )
fi

для вывода остальных элементов массива в виде списка, разделенного запятыми -после двоеточия:

localhost: alpha,beta,gamma,theta
dns: itsy,bitsy,spider man

Примечание. :Ни одно из приведенных выше цитирований не является случайным. Заключение расширения массива гарантирует, что каждый элемент заключен в кавычки отдельно. Заключение раскрытия одного элемента в кавычки гарантирует, что оболочка не разбивает значение на пробелы (и не выполняет подстановку имен файлов на разделенные -слова вверх ).

См. также


Вы также можете сделать это с /bin/shбез именованных массивов. Вместо этого мы используем список позиционных параметров для хранения наших данных и заканчиваем каждый раздел этого списка специальным словом (мы используем END, это произвольная строка, которую мы выбираем ).Затем мы перебираем этот список и сдвигаем элементы из него по мере продвижения:

#!/bin/sh

set --  127.0.0.1 localhost alpha beta gamma theta END \
        8.8.8.8   dns       itsy bitsy 'spider man' END

while [ "$#" -gt 0 ]; do
        if ping -c 3 "$1" >/dev/null; then
                printf '%s\n' "$2"
                shift 2
                while [ "$1" != END ]; do
                        printf '\t%s\n' "$1"
                        shift
                done
        else
                while [ "$1" != END ]; do shift; done
        fi

        shift
done
7
18.03.2021, 22:24
for n in ${A} ${B} ;  do

Это не то, что вы хотите. Взять массив без индекса — это то же самое, что взять элемент с нулевым индексом, поэтому ${A}— это то же самое, что и ${A[0]}, и то же самое для B. Цикл устанавливает $nв отдельные значения одно за другим, так что вы получите ${A[0]}на первой итерации и ${B[0]}на второй.

То же самое относится к ${n[0]}, ${n[1]}, ${n[2]}и т. д. $nне является массивом, поэтому только ${n[0]}имеет какое-либо содержимое, остальные просто не заданы.

Bash на самом деле не работает с двумерными -массивами, но вы можете инвертировать эту структуру с помощью чего-то вроде этого:

ips=(127.0.0.1 8.8.8.8)
names=(localhost dns)
for i in "${!ips[@]}"; do
   if ping -c3 "${ips[i]}"; then
       echo "host ${names[i]-"(unknown")} is up"
   fi
done

Но вам, вероятно, следует просто хранить данные в отдельном файле.


Обратите внимание, что все, что касается структур данных и циклов, может сильно отличаться в Zsh, а IIRC ksh также имеет лучшую поддержку структур данных, чем Bash.

4
18.03.2021, 22:24

Теги

Похожие вопросы