Как перебирать массив bash при удалении элемента?

GNU bash , версия 4.3.42 (1) -release (arm-openwrt-linux-gnu)

Я пытаюсь создать скрипт правила iptables, используя массив bash.

$ NETID используется для создания сетевых адресов. Например, 192.168. 10 .0 / 24, 192.168. 20 .0 / 24 и т. Д.

Мне нужна помощь в цикле for.

Я пытаюсь создать временную переменную ($ NETID_TMP), где «i» в $ {NETID [@]} удаляется из $ NETID_TMP. Например, если «i» = 10, NETID_TMP = (20 30 40).

Автор сценария:

#!/opt/bin/bash NETID=(10 20 30 40) for (( i=0; i<${#NETID[@]}; i++ )); do NETID_TMP=(${NETID[*]}) unset NETID_TMP[${NETID[i]}] iptables -I FORWARD -s 192.168.${NETID[i]}.0/24 -d 192.168.${NETID_TMP[0]}.0/24,192.168.${NETID_TMP[1]}.0/24,192.168.${NETID_TMP[2]}.0/24 -j DROP done 

Результат должен быть …

 iptables -I FORWARD -s 192.168.10.0/24 -d 192.168.20.0/24,192.168.30.0/24,192.168.40.0/24 -j DROP iptables -I FORWARD -s 192.168.20.0/24 -d 192.168.10.0/24,192.168.30.0/24,192.168.40.0/24 -j DROP iptables -I FORWARD -s 192.168.30.0/24 -d 192.168.10.0/24,192.168.20.0/24,192.168.40.0/24 -j DROP iptables -I FORWARD -s 192.168.40.0/24 -d 192.168.10.0/24,192.168.20.0/24,192.168.30.0/24 -j DROP 

 netid=(10 20 30 40) for i in "${netid[@]}"; do out="iptables -I FORWARD -s 192.168.$i.0/24 -d " for j in "${netid[@]}"; do (( i == j )) && continue out+="192.168.$j.0/24," done out="${out%,} -j DROP" printf '%s\n' "$out" done 

Этот скрипт Bash производит

 iptables -I FORWARD -s 192.168.10.0/24 -d 192.168.20.0/24,192.168.30.0/24,192.168.40.0/24 -j DROP iptables -I FORWARD -s 192.168.20.0/24 -d 192.168.10.0/24,192.168.30.0/24,192.168.40.0/24 -j DROP iptables -I FORWARD -s 192.168.30.0/24 -d 192.168.10.0/24,192.168.20.0/24,192.168.40.0/24 -j DROP iptables -I FORWARD -s 192.168.40.0/24 -d 192.168.10.0/24,192.168.20.0/24,192.168.30.0/24 -j DROP 

через двойной цикл. Внутренний цикл пропускает итерации, где переменная цикла численно равна переменной цикла внешнего цикла для создания желаемого эффекта.

Если у вас есть строка в $out , вы можете ее eval .

Альтернативно, чтобы избежать eval :

 netid=(10 20 30 40) for i in "${netid[@]}"; do sarg="192.168.$i.0/24" darg="" for j in "${netid[@]}"; do (( i == j )) && continue darg+="192.168.$j.0/24," done darg="${darg%,}" iptables -I FORWARD -s "$sarg" -d "$darg" -j DROP done 

Переменная подстановка ${parameter%word} приведет к удалению word с самого конца $parameter , поэтому darg="${darg%,}" удалит запятую в конце af $darg .


Обновление после вопроса в комментарии:

Единственное, что связано с bash – это использование массива $netid , оператора += для добавления в строку $darg и ((...)) . Мы можем превратить это в сценарий sh как это (здесь, полагая, что $IFS прежнему имеет значение по умолчанию):

 netid="10 20 30 40" for i in $netid; do sarg="192.168.$i.0/24" darg="" for j in $netid; do [ "$i" -eq "$j" ] && continue darg="${darg}192.168.$j.0/24," done darg="${darg%,}" iptables -I FORWARD -s "$sarg" -d "$darg" -j DROP done 

Если у вас есть отдельный список номеров, которые вы хотите подключить для $darg , используйте это во внутреннем цикле вместо $netid . Это должна быть строка номеров, разделенных пробелами.

С zsh проще:

 #! /bin/zsh - nets=(192.168.{10,20,30,40}.0/24) for net ($nets) iptables -I FORWARD -s $net -d ${(j:,:)nets:#$net} -j DROP