简体   繁体   English

bash 中的数组循环问题

[英]issues looping over array in bash

I have written a very simple port scanning script in bash. For my ports argument, i accept a single port (eg: 443), a comma-separated list (eg: 80,443), or a range (eg: 1-1000).我在 bash 中编写了一个非常简单的端口扫描脚本。对于我的端口参数,我接受单个端口(例如:443)、逗号分隔列表(例如:80,443)或一个范围(例如:1-1000)。

When I run my script with a single port or comma-separated list of ports, everything runs as it should:当我使用单个端口或以逗号分隔的端口列表运行我的脚本时,一切都按预期运行:

~/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.

However, when I run with a range, I get:但是,当我使用范围运行时,我得到:

~/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.

Relevant code where I assign the array:我分配数组的相关代码:

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

and the loop itself:和循环本身:

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

I'm not sure if this is because of how I'm assigning my port array, or if it is because of something I have wrong in my loop.我不确定这是因为我分配端口阵列的方式,还是因为我在循环中出错。 I'm relatively new to bash scripting, and I'm having a terrible time figuring out what I have wrong.我是 bash 脚本编写的新手,而且我很难弄清楚我做错了什么。 I've read here on SO about some commands run in loops eating the output of other portions of the script, but I don't believe that to be the case here, as the script does actually print to screen, just not as expected.我在 SO 上读到过一些循环运行的命令吃掉了脚本其他部分的 output,但我不认为这里是这种情况,因为脚本确实打印到屏幕上,只是不符合预期。

EDIT: Here is the full script:编辑:这是完整的脚本:

#!/bin/bash

verbose=0
while [ "$1" != "" ]; do
    case "$1" 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="$2";    shift;;
        -p | --ports )
ports="$2"; 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."

Addressing just the portarray=(...) assignment (when ports=1-10 )处理portarray=(...)分配(当ports=1-10时)

Consider:考虑:

$ 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')

NOTE: the output from the $(seq...) call is processed as a single string with embedded linefeeds.注意:来自$(seq...)调用的 output 被处理为带有嵌入式换行符的单个字符串。

A couple ideas:几个想法:

### 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")

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM