bash 中的数组循环问题
issues looping over array in bash
我在bash写了一个很简单的端口扫描脚本。对于我的端口参数,我接受单个端口(例如:443)、逗号分隔列表(例如:80,443)或范围(例如:1-1000)。
当我 运行 我的脚本带有单个端口或以逗号分隔的端口列表时,一切 运行 都应该如此:
~/projects/bash-port-scan# ./bash-port-scan.sh -i xx.xx.xxx.xxx -p 1,80,443 -v
Beginning scan of xx.xx.xxx.xxx
Port 1 closed
Port 80 open
Port 443 open
Scan complete.
~/projects/bash-port-scan# ./bash-port-scan.sh -i xx.xx.xxx.xxx -p 80 -v
Beginning scan of xx.xx.xxx.xxx
Port 80 open
Scan complete.
然而,当我 运行 有一个范围时,我得到:
~/projects/bash-port-scan# ./bash-port-scan.sh -i xx.xx.xxx.xxx -p 1-10 -v
Beginning scan of xx.xx.xxx.xxx
Port 1
2
3
4
5
6
7
8
9
10 closed
Scan complete.
我分配数组的相关代码:
portarray=()
if [[ "$ports" == *","* ]]; then
IFS=','
read -r -a portarray <<< $ports
IFS=' '
elif [[ "$ports" == *"-"* ]]; then
IFS='-'
read -r -a range <<< $ports
IFS=' '
first="${range[0]}"
last="${range[1]}"
portarray=($(seq $first 1 $last))
else
portarray=($ports)
fi
和循环本身:
empty=""
for p in "${portarray[@]}"; do
result=$(nc -zvw5 $ip $p 2>&1 | grep open)
if [ "$result" = "$empty" ]; then
if [ $verbose -eq 1 ]; then
str="Port "
closed=" closed"
echo "$str$p$closed"
fi
else
str="Port "
closed=" open"
echo "$str$p$closed"
fi
done
我不确定这是因为我分配端口阵列的方式,还是因为我在循环中出错。我对 bash 脚本编写还比较陌生,而且我很难弄清楚我哪里出了问题。
我在 SO 上读到过一些命令 运行 在循环中吃掉脚本其他部分的输出,但我不相信这里是这种情况,因为脚本确实打印到屏幕上,只是不如预期。
编辑:
这是完整的脚本:
#!/bin/bash
verbose=0
while [ "" != "" ]; do
case "" in
-h | --help )
echo "bash-port-scan.sh v0.1\r\nUsage: ./bash-port-scan.sh -i 127.0.0.1 -p 80,443\r\n./bash-port-scan.sh -i 127.0.0.1 -p 1-1000"; shift;;
-v | --verbose )
verbose=1; shift;;
-i | --ip )
ip=""; shift;;
-p | --ports )
ports=""; shift;;
esac
shift
done
if [[ $ip = "" ]]; then
echo "Please enter an IP address with -i"
exit
fi
if [[ $ports = "" ]]; then
echo "Please enter the port(s) with -p"
exit
fi
portarray=()
if [[ "$ports" == *","* ]]; then
IFS=','
read -r -a portarray <<< $ports
IFS=' '
elif [[ "$ports" == *"-"* ]]; then
IFS='-'
read -r -a range <<< $ports
IFS=' '
first="${range[0]}"
last="${range[1]}"
portarray=($(seq $first $last))
else
portarray=($ports)
fi
if [ $verbose -eq 1 ]; then
echo "Beginning scan of $ip"
fi
shuf -e "${portarray[@]}"
empty=""
for p in "${portarray[@]}"; do
result=$(nc -zvw5 $ip $p 2>&1 | grep open)
if [ "$result" = "$empty" ]; then
if [ $verbose -eq 1 ]; then
str="Port "
closed=" closed"
echo "$str$p$closed"
fi
else
str="Port "
closed=" open"
echo "$str$p$closed"
fi
done
echo "Scan complete."
解决只是 portarray=(...)
作业(当ports=1-10
)
考虑:
$ first=1
$ last=10
$ portarray=($(seq $first 1 $last))
$ typeset -p portarray
declare -a portarray=([0]=$'1\n2\n3\n4\n5\n6\n7\n8\n9\n10')
注意: $(seq ...)
调用的输出被处理为带有嵌入式换行符的单个字符串。
几个想法:
### define \n as field separator; apply custom IFS in same line to limit IFS change to just the follow-on array assignment:
$ IFS=$'\n' portarray=($(seq $first 1 $last))
$ typeset -p portarray
declare -a portarray=([0]="1" [1]="2" [2]="3" [3]="4" [4]="5" [5]="6" [6]="7" [7]="8" [8]="9" [9]="10")
### use mapfile to read each *line* into a separate array entry:
$ mapfile -t portarray < <(seq $first 1 $last)
$ typeset -p portarray
declare -a portarray=([0]="1" [1]="2" [2]="3" [3]="4" [4]="5" [5]="6" [6]="7" [7]="8" [8]="9" [9]="10")
我在bash写了一个很简单的端口扫描脚本。对于我的端口参数,我接受单个端口(例如:443)、逗号分隔列表(例如:80,443)或范围(例如:1-1000)。
当我 运行 我的脚本带有单个端口或以逗号分隔的端口列表时,一切 运行 都应该如此:
~/projects/bash-port-scan# ./bash-port-scan.sh -i xx.xx.xxx.xxx -p 1,80,443 -v
Beginning scan of xx.xx.xxx.xxx
Port 1 closed
Port 80 open
Port 443 open
Scan complete.
~/projects/bash-port-scan# ./bash-port-scan.sh -i xx.xx.xxx.xxx -p 80 -v
Beginning scan of xx.xx.xxx.xxx
Port 80 open
Scan complete.
然而,当我 运行 有一个范围时,我得到:
~/projects/bash-port-scan# ./bash-port-scan.sh -i xx.xx.xxx.xxx -p 1-10 -v
Beginning scan of xx.xx.xxx.xxx
Port 1
2
3
4
5
6
7
8
9
10 closed
Scan complete.
我分配数组的相关代码:
portarray=()
if [[ "$ports" == *","* ]]; then
IFS=','
read -r -a portarray <<< $ports
IFS=' '
elif [[ "$ports" == *"-"* ]]; then
IFS='-'
read -r -a range <<< $ports
IFS=' '
first="${range[0]}"
last="${range[1]}"
portarray=($(seq $first 1 $last))
else
portarray=($ports)
fi
和循环本身:
empty=""
for p in "${portarray[@]}"; do
result=$(nc -zvw5 $ip $p 2>&1 | grep open)
if [ "$result" = "$empty" ]; then
if [ $verbose -eq 1 ]; then
str="Port "
closed=" closed"
echo "$str$p$closed"
fi
else
str="Port "
closed=" open"
echo "$str$p$closed"
fi
done
我不确定这是因为我分配端口阵列的方式,还是因为我在循环中出错。我对 bash 脚本编写还比较陌生,而且我很难弄清楚我哪里出了问题。 我在 SO 上读到过一些命令 运行 在循环中吃掉脚本其他部分的输出,但我不相信这里是这种情况,因为脚本确实打印到屏幕上,只是不如预期。
编辑: 这是完整的脚本:
#!/bin/bash
verbose=0
while [ "" != "" ]; do
case "" in
-h | --help )
echo "bash-port-scan.sh v0.1\r\nUsage: ./bash-port-scan.sh -i 127.0.0.1 -p 80,443\r\n./bash-port-scan.sh -i 127.0.0.1 -p 1-1000"; shift;;
-v | --verbose )
verbose=1; shift;;
-i | --ip )
ip=""; shift;;
-p | --ports )
ports=""; shift;;
esac
shift
done
if [[ $ip = "" ]]; then
echo "Please enter an IP address with -i"
exit
fi
if [[ $ports = "" ]]; then
echo "Please enter the port(s) with -p"
exit
fi
portarray=()
if [[ "$ports" == *","* ]]; then
IFS=','
read -r -a portarray <<< $ports
IFS=' '
elif [[ "$ports" == *"-"* ]]; then
IFS='-'
read -r -a range <<< $ports
IFS=' '
first="${range[0]}"
last="${range[1]}"
portarray=($(seq $first $last))
else
portarray=($ports)
fi
if [ $verbose -eq 1 ]; then
echo "Beginning scan of $ip"
fi
shuf -e "${portarray[@]}"
empty=""
for p in "${portarray[@]}"; do
result=$(nc -zvw5 $ip $p 2>&1 | grep open)
if [ "$result" = "$empty" ]; then
if [ $verbose -eq 1 ]; then
str="Port "
closed=" closed"
echo "$str$p$closed"
fi
else
str="Port "
closed=" open"
echo "$str$p$closed"
fi
done
echo "Scan complete."
解决只是 portarray=(...)
作业(当ports=1-10
)
考虑:
$ first=1
$ last=10
$ portarray=($(seq $first 1 $last))
$ typeset -p portarray
declare -a portarray=([0]=$'1\n2\n3\n4\n5\n6\n7\n8\n9\n10')
注意: $(seq ...)
调用的输出被处理为带有嵌入式换行符的单个字符串。
几个想法:
### define \n as field separator; apply custom IFS in same line to limit IFS change to just the follow-on array assignment:
$ IFS=$'\n' portarray=($(seq $first 1 $last))
$ typeset -p portarray
declare -a portarray=([0]="1" [1]="2" [2]="3" [3]="4" [4]="5" [5]="6" [6]="7" [7]="8" [8]="9" [9]="10")
### use mapfile to read each *line* into a separate array entry:
$ mapfile -t portarray < <(seq $first 1 $last)
$ typeset -p portarray
declare -a portarray=([0]="1" [1]="2" [2]="3" [3]="4" [4]="5" [5]="6" [6]="7" [7]="8" [8]="9" [9]="10")