如何在跳过其中一个值的同时迭代变量?

如何在跳过其中一个值的同时迭代变量?

使用内核 2.6.x

笔记:在上一个问题中,我尝试使用 bash 数组来完成此任务。在此操作系统上,非 sh shell 作为 Entware-NG 包安装,并且无法使用,因为它们在脚本运行后加载。

目标:使用脚本创建 iptables 规则(不是 bash、zsh 等)具有以下格式。对于 $NAME 中的每个接口...

  • 从 $NETID 中的相应位置为其源网络 IP 地址创建规则。 $NAME 中的第一个值具有根据 $NETID 中的第一个值构建的源网络地址。例如,-i eth1 对应于 -src 192.168.10.0/24,-i eth2 对应于 192.168.20.0/24。
  • 为 $NETID 中未在源网络地址中使用的值构建目标 IP 地址。例如,-src 192.168.10.0/24 -dst 192.168.20.0/24。
  • 使用一个目标网络地址构建每条规则。不要为 -dst 值指定多个地址。

期望的结果:

iptables -I FORWARD -i eth1 -src 192.168.10.0/24 -dst 192.168.20.0/24
iptables -I FORWARD -i eth1 -src 192.168.10.0/24 -dst 192.168.30.0/24
iptables -I FORWARD -i eth2 -src 192.168.20.0/24 -dst 192.168.10.0/24
iptables -I FORWARD -i eth2 -src 192.168.20.0/24 -dst 192.168.30.0/24
iptables -I FORWARD -i eth3 -src 192.168.30.0/24 -dst 192.168.10.0/24
iptables -I FORWARD -i eth3 -src 192.168.30.0/24 -dst 192.168.20.0/24

问题:下面的脚本不会创建接口名称和 -src 值的 1:1 关系。例如,eth1 和 192.168。10.0/24、eth2 和 192.168。20.0/24等


#!/bin/sh
NETID="10 20 30"
NAME="eth1 eth2 eth3"



for i in $NETID; do
   sarg="192.168.$i.0/24"
   darg=""

   for j in $NETID; do
      [ "$i" -eq "$j" ] && continue
      darg="192.168.$j.0/24"
      for k in $NAME; do
         echo "iptables -I FORWARD -i $k -s $sarg -d $darg -j DROP"
      done
   done
done

答案1

使用 POSIX Awk,而不用担心输出行的顺序:

echo | awk -v 'netids=10 20 30' -v 'names=eth1 eth2 eth3' '
  BEGIN {
    netidcount = split(netids, netid)
    namecount = split(names, name)
    if (netidcount != namecount) {
      print "Error: You must pass the same number of names and netids."
      exit 1
    }
    for (i in name) {
      for (j in name) {
        if (i == j) {
          continue
        }
        printf "iptables -I FORWARD -i %s -src 192.168.%s.0/24 -dst 192.168.%s.0/24 -j DROP\n", name[i], netid[i], netid[j]
      }
    }
  }'

当然你可以改变这个;例如,您可以将上面的 awk 脚本放入文件中并使用以下命令调用它:

echo | awk -v ... -v ... -f script.awk

或者,您可以将网络 ID 和名称放入 中data.txt,格式可能如下:

eth1 10
eth2 20
eth3 30

script.awk并像这样编写 Awk 脚本文件:

#!/bin/awk
NF != 2 {
  error = 1
  print "Error: Each line of input must contain one interface name and one number."
  exit
}
(($1 in nameseen) || ($2 in netidseen)) {
  error = 1
  print "Error: Each interface name and network id in input must be unique."
  exit
}
{
  name[NR] = $1
  netid[NR] = $2
  nameseen[$1]
  netidseen[$2]
}
END {
  if (error) {
    exit error
  }
  for (i in name) {
    for (j in name) {
      if (i == j) {
        continue
      }
      printf "iptables -I FORWARD -i %s -src 192.168.%s.0/24 -dst 192.168.%s.0/24 -j DROP\n", name[i], netid[i], netid[j]
    }
  }
}

然后这样称呼它:

awk -f script.awk data.txt

有很多选择。

我可能会data.txt看起来像:

eth1 192.168.10.0/24
eth2 192.168.20.0/24
eth3 192.168.30.0/24

修改脚本以使用此数据格式作为练习留给读者。 ;)


(注意:如果您使用此代码,请记住包含此答案的链接作为归属。)

答案2

POSIX sh 版本:

for n in 1 2 3; do
  src="$n"0
  interface="eth$n"
  for dest in 10 20 30; do
    [ "$dest" = "$src" ] && continue
    printf 'iptables -I FORWARD -i %s -src %s -dst %s -j DROP\n' "$interface" "192.168.$src.0/24" "192.168.$dest.0/24"
  done
done

我对如何将接口与规则源联系起来做了一些假设。但这确实会产生您所显示的所需输出。

答案3

您的主要问题是,接口名称(eth1、eth2、...)和关联网络(10.x、20.x、...)之间存在 1:1 关系,但您的代码中没有任何内容明确建立了 1:1 关系。

你必须在某个地方绑定ethN192.168.N.x.您可以使用包含一个对列表的单个NETS变量来完成此操作interface:N。请参阅下面我的代码:

#!/bin/sh
NETS="eth1:10 eth2:20 eth3:30"

for net in $NETS; do
    k="${net%:*}"          # Yields the name of the interface
    i="${net#*:}"          # Yields the number bound to the interface
    sarg="192.168.$i.0/24"

    for net in $NETS; do
        j="${net#*:}"      # Yields the number bound to the interface
        [ "$i" -eq "$j" ] && continue
        darg="192.168.$j.0/24"
        echo "iptables -I FORWARD -i $k -s $sarg -d $darg -j DROP"
    done
done

笔记:

  • for k in $NAME完全删除了循环,因为它似乎与您想要实现的目标相矛盾。
  • 您甚至可以将网络放入定义中并直接从中NETS读取。就像是:sargdarg

    NETS="eth1:192.168.10.0/24 eth2:192.168.20.0/24 eth3:192.168.30.0/24"
    

[更新] 结合我对你另一个问题的回答,这将给出这个脚本:

#!/bin/sh
NETID="10 20 30"
NAME="eth1 eth2 eth3"
NETS=$(set -- $NETID; for iface in $NAME; do echo "$iface:192.168.$1.0/24"; shift; done)

for net in $NETS; do
    iface="${net%:*}"
    sarg="${net#*:}"

    for net in $NETS; do
        darg="${net#*:}"
        [ "$sarg" != "$darg" ] && echo "iptables -I FORWARD -i $k -s $sarg -d $darg -j DROP"
    done
done

相关内容