Linux 環境でも範囲指定して ping できるので、何気に便利

Linux 環境で ping 打つときは、IP か FQDN を指定して その都度 Loop を回すか、単発で打つことがほとんどだと思います。

Windows 環境だと業務用としても愛用されているお馴染みの exping というのがあって、Text Area にリストを貼り付けて実行すると次々と ping を打ってくれて非常に便利ですと。

それが Linux 環境となると ping コマンドはあるものの、次々と ping してくれる機能はなく、

そこまで高機能でなくても、 exping みたいにリスト化した IP とか FQDN に対して、自動的に ping してくれたら嬉しいけどなーと思うときがあります。

それに加えて、例えば、コマンドライン上で IP・FQDN に対して、「10番 ~ 20番」までとか特定範囲を指定してポンと叩いた時に 自動的に ping 出来るようになると さらに便利そうだなと思って作りました。

動作としてはシンプルで、以下のようなことができます。

  • 範囲を指定したい場合は、[1-10] のように括弧で囲う
  • $ rping.sh 10.10.10.[10-20] のように実行した場合は、連番の IP リストへ ping
  • $ rping.sh example-[01-09].com のように実行した場合は、連番の FQDN リストへ ping
  • $ rping.sh list.txt のように、ファイル指定も可能 (範囲指定記述 / コメント入力可能)
  • 後は、取得したリストをもとに、順番通りに ping を打って行く
  • 結果は、○ × で表示されます

一応、Usage 出力するようになっているので、とりあえず実行してみてください。

rping.sh (range ping の略)
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
#!/bin/bash

####「Ctrl + C」で 終了 (Ping Loop から抜ける)
trap "exit 0" 2

########################
#### 設定パラメータ ####
########################
readonly PING_RETRY=1    #### Ping 失敗時の再試行回数

################
#### Global ####
################
_IP_FQDN=()              #### Array
_COMMENT=()              #### Array
_IDX=0                   #### Int「_IP_FQDN / _COMMENT」List の共通 Index

###############
#### Usage ####
###############
function _usage()
{
    cat <<END
-------------------------------------------------------------------------------------
Usage 1: $0 aaa.bbb.ccc.ddd
  [ex] $0 10.10.10.11
  [ex] $0 10.10.10.[1-254]

Usage 2: $0 host.domain
  [ex] $0 www.example.com
  [ex] $0 www.example-[01-03].com

Usage 3: $0 <FILE>            : 各行ごとに「#」でコメント入力可能
  [ex] $0 ./ip_list.txt
  [ex] $0 ./fqdn_list.txt
  [ex] $0 ./ip_fqdn_list.txt

Usage 4: $0 <IP Range | FQDN Range | FILE> -l   : 「-l」指定でリストのみ出力 (dry-run 的な)
  [ex] $0 10.10.10.[1-254] -l
  [ex] $0 www.example-[01-03].com -l
  [ex] $0 ./list.txt -l

NG:
  [IP]
    [ng] $0 10.10.256.11      : OCT 範囲外
    [ng] $0 10.10.30.256      : OCT 範囲外
    [ng] $0 10.10.30.[1-256]  : OCT 範囲外
    [ng] $0 10.10.30.[11-12   : 不正な範囲指定
    [ng] $0 10.10.30.11-12]   : 不正な範囲指定
    [ng] $0 10.10.30.[12-11]  : 不正な範囲指定
    [ng] $0 10.10.[30-31].11  : 範囲指定は 4番目の OCT のみ可能
    [ng] $0 10.10.30.0/24     : Segment 単位で指定不可

  [FQDN]
    [ng] $0 www.example[1-03].com           : 桁数不一致
    [ng] $0 www.example[03-01].com          : 不正な範囲指定
    [ng] $0 hoge[01-03].example[01-03].com  : 複数の範囲指定不可
-------------------------------------------------------------------------------------
END

    exit 1
}

########################
#### IP / FQDN 判定 ####
########################
####「.」付の「Alphabet」2文字以上で終われば FQDN とし、
#### それ以外、4 OCT の数字形式で構成されている場合は、IP とする。
####
#### また、IP の 各 OCT の 0-255 範囲正常性 等 細かいチェックは「_get_ip_list()」にて実施
####
#### Return (String) : [IP | FQDN]
function _get_type()
{
    [ $# -eq 0 ] && _usage

    local PARAM="$1"
    local TMP=""
    local RES=""

    echo "$PARAM" | grep -i "\.[a-z]\{2,\}$" > /dev/null 2>&1

    if [ $? -eq 0 ]; then

        RES="FQDN"

    else

        #### Range 指定の場合は、Range を外して、開始番号で IP 取得
        TMP=`echo "$PARAM" | tr -d "[]" | sed 's/-.*//g'`    #### 10.10.30.[1-254] -> 10.10.30.1

        echo "$TMP" | grep "^\([0-9]\{1,3\}\.\)\{3,\}[0-9]\{1,3\}$" > /dev/null 2>&1
        [ $? -eq 0 ] && RES="IP"

    fi

    echo "$RES"
}

############################
#### IP / IP リスト取得 ####
############################
#### 無効な IP は、ping 実行時に弾かれるので、
#### IP / IP Range の正常性チェックに関しては、最低限のチェックのみ実施する。
####
#### パラメータに無効な IP / IP Range がセットされたとしても、
#### ここでは 細かいチェックは実施せず、単一 IP / IP リストを取得することに注力する。
####
#### $1 : aaa.bbb.ccc.ddd
####      aaa.bbb.ccc.[nnn-nnn]
function _get_ip_list()
{
    [ $# -eq 0 ] && _usage

    local PARAM=$@

    local IP=`echo $PARAM | cut -d ' ' -f1`       #### 10.10.30.11 or 10.10.30.[1-254]
    local CMT=`echo $PARAM | cut -d ' ' -f2-`     #### コメント取得

    [ "$IP" = "$CMT" ] && CMT=""                  #### コメントが空の場合 "" で初期化 (Option)
    [ -z "${CMT}" ] && _COMMENT[$i]=""            #### コメントが空の場合 "" で初期化 (必須)

    ####「.」区切りの 4 フィールドで構成されているか
    local OCT_CNT=`echo $IP | sed 's/\./\n/g' | wc -l`
    [ $OCT_CNT -ne 4 ] && _usage

    #### 各 OCT 取得 ################################# 10.10.30.11 or 10.10.30.[1-254]
    local OCT_1=`echo "$IP" | cut -d '.' -f1`     #### 10
    local OCT_2=`echo "$IP" | cut -d '.' -f2`     #### 10
    local OCT_3=`echo "$IP" | cut -d '.' -f3`     #### 30
    local OCT_4=`echo "$IP" | cut -d '.' -f4`     #### 11 or [1-254]

    #### 4 OCT 分の中身が取得できていなければ、NG
    [ -z "$OCT_1" -o -z "$OCT_2" -o -z "$OCT_3" -o -z "$OCT_4" ] && _usage

    #### OCT_1 ~ OCT_3 の値に数字以外の文字が存在する場合、NG
    #### OCT_1 ~ OCT_3 の値が 0~255 範囲外なら NG
    #### OCT_4 については、IP か IP Range かによってチェック処理を分ける必要があるので、後で別途チェックする
    i=0
    for i in $OCT_1 $OCT_2 $OCT_3
    do
        echo "$i" | grep "[^0-9]" > /dev/null 2>&1; [ $? -eq 0 ] && _usage
        [ ! \( $i -ge 0 -a $i -le 255 \) ] && _usage
    done

    #### IP / IP Range 処理分け
    #### OCT_4 に数字 3桁の [nnn-nnn] 形式が指定された場合は IP Range
    echo "$OCT_4" | grep "\[[0-9]\{1,3\}-[0-9]\{1,3\}\]" > /dev/null 2>&1

    #### Range 指定なし : 単独 IP そのまま取得
    if [ $? -ne 0 ]; then

        #### OCT_4 の値に数字以外の文字が存在する場合は、NG
        #### OCT_4 の値が 0~255 範囲外の場合は、NG
        echo "$OCT_4" | grep "[^0-9]" > /dev/null 2>&1; [ $? -eq 0 ] && _usage
        [ ! \( $OCT_4 -ge 0 -a $OCT_4 -le 255 \) ] && _usage

        #### 単一 IP 取得
        _IP_FQDN[$_IDX]=$IP
        _COMMENT[$_IDX]=$CMT

        [ "${_IP_FQDN[$_IDX]}" = "${_COMMENT[$_IDX]}" ] && _COMMENT[$_IDX]=""

       _IDX=`expr $_IDX + 1`

    #### Range 指定あり : IP リスト作成
    else

        #### IP Range (開始~終了番号) 取得
        OCT_4=`echo "$OCT_4" | tr -d "[]"`        #### 1-254

        local S=`echo $OCT_4 | cut -d '-' -f1`    #### 1
        local E=`echo $OCT_4 | cut -d '-' -f2`    #### 254

        #### 以下、NG とする
        #### - 開始番号の値が 0~255 範囲外の場合
        #### - 終了番号の値が 0~255 範囲外の場合
        #### - 開始番号が終了番号より大きい場合
        [ ! \( $S -ge 0 -a $S -le 255 \) ] && _usage
        [ ! \( $E -ge 0 -a $E -le 255 \) ] && _usage
        [ $S -gt $E ] && _usage

        #### IP Range のリスト取得
        local OCT_4=0

        for OCT_4 in `seq $S 1 $E`
        do
            _IP_FQDN[$_IDX]=${OCT_1}.${OCT_2}.${OCT_3}.${OCT_4}
            _COMMENT[$_IDX]=$CMT

            [ "${_IP_FQDN[$_IDX]}" = "${_COMMENT[$_IDX]}" ] && _COMMENT[$_IDX]=""

           _IDX=`expr $_IDX + 1`
        done

    fi
}

################################
#### FQDN / FQDN リスト取得 ####
################################
#### 無効な FQDN は、ping 実行時に弾かれるので、
#### FQDN / FQDN Range の正常性チェックに関しては、最低限のチェックのみ実施する。
####
#### IP リスト取得時の処理とは違って、FQDN の Range は文字列として扱うため、
#### FQDN Range は桁数を合わせて指定するようにしている。
####
#### $1 : hoge.example-[1-9].com
####      hoge.example-[01-10].com
function _get_fqdn_list()
{
    [ $# -eq 0 ] && _usage

    local PARAM=$@

    local FQDN=`echo $PARAM | cut -d ' ' -f1`     #### example-[101-110].com
    local CMT=`echo $PARAM | cut -d ' ' -f2-`     #### コメント取得

    [ "$FQDN" = "$CMT" ] && CMT=""                #### コメントが空の場合 "" で初期化 (Option)
    [ -z "${CMT}" ] && _COMMENT[$i]=""            #### コメントが空の場合 "" で初期化 (必須)

    #### FQDN / FQDN Range 処理分け
    #### [n-n] 形式が含まれていれば、Range (n: 1桁以上の数字)
    echo "$FQDN" | grep "\[[0-9]\{1,\}-[0-9]\{1,\}\]" > /dev/null 2>&1

    #### Range 指定なし : 単独 FQDN そのまま取得
    if [ $? -ne 0 ]; then

        #### 単一 FQDN 取得
        _IP_FQDN[$_IDX]=$FQDN
        _COMMENT[$_IDX]=$CMT

        [ "${_IP_FQDN[$_IDX]}" = "${_COMMENT[$_IDX]}" ] && _COMMENT[$_IDX]=""

       _IDX=`expr $_IDX + 1`

    #### Range 指定あり : FQDN リスト作成
    else

        #### N-N を軸として FQDN をばらす ################################## example-[101-110].com
        local TMP_1=`echo "$FQDN" | cut -d "[" -f1`                     #### example-
        local RANGE=`echo "$FQDN" | cut -d "[" -f2 | cut -d "]" -f1`    #### 101-110
        local TMP_2=`echo "$FQDN" | cut -d "]" -f2`                     #### .com

        [ -z "$TMP_1" -o -z "$RANGE" -o -z "$TMP_2" ] && _usage
        echo "$TMP_1" | egrep "(\[|\])" > /dev/null 2>&1; [ $? -eq 0 ] && _usage
        echo "$TMP_2" | egrep "(\[|\])" > /dev/null 2>&1; [ $? -eq 0 ] && _usage

        #### FQDN Range (開始~終了番号) 取得
        local S=`echo $RANGE | cut -d '-' -f1`        #### 101
        local E=`echo $RANGE | cut -d '-' -f2`        #### 110

        #### 開始番号 / 終了番号の桁数取得
        local S_LEN=`echo "$S" | wc -L`               #### 3
        local E_LEN=`echo "$E" | wc -L`               #### 3

        #### 以下、NG とする
        #### - 開始番号に数字以外の文字が入っている場合
        #### - 終了番号に数字以外の文字が入っている場合
        #### - 開始番号と終了番号の桁数が一致しない場合
        #### - 開始番号が終了番号より大きい場合
        echo "$S" | grep "[^0-9]" > /dev/null 2>&1; [ $? -eq 0 ] && _usage
        echo "$E" | grep "[^0-9]" > /dev/null 2>&1; [ $? -eq 0 ] && _usage
        [ $S_LEN -ne $E_LEN ] && _usage
        [ $S -gt $E ] && _usage

        #### FQDN Range のリスト取得
        #### Range の頭に「0」が付いている場合は「0」埋めリストが作成される
        local FQDN=""

        for i in `seq -f %0${S_LEN}g $S 1 $E`
        do
            _IP_FQDN[$_IDX]=${TMP_1}${i}${TMP_2}
            _COMMENT[$_IDX]=$CMT

            [ "${_IP_FQDN[$_IDX]}" = "${_COMMENT[$_IDX]}" ] && _COMMENT[$_IDX]=""

           _IDX=`expr $_IDX + 1`
        done

    fi
}

#### IP / IP Range、FQDN / FQDN Range リスト取得
#### - 単発コマンドの場合、コマンド実行時に入力された IP、または FQDN からリスト取得
#### - ファイル指定の場合、コマンド実行時の指定ファイルから コメント付きの IP、または FQDN リスト取得
function _get_list()
{
    local PARAM="$1"
    local TYPE=""

    #### ファイル指定の場合
    #### 1行ずつ 読み込みながら「コメント付き」のIP / IP リスト、FQDN / FQDN リスト取得
    if [ -f "$PARAM" ]; then

        local FILE=$PARAM
        local DATA=`cat $FILE | grep -v "^#" | grep -v "^$"`

        while read line
        do

            PARAM=`echo $line | cut -d ' ' -f1`    #### コメント除去後、PARAM 上書き (TYPE 取得用)
            TYPE=`_get_type "$PARAM"`              #### $PARAM : IP / IP Range or FQDN / FQDN Range

            if   [ "$TYPE" = "IP" ];    then  _get_ip_list "$line";      #### IP
            elif [ "$TYPE" = "FQDN" ];  then  _get_fqdn_list "$line";    #### FQDN
            else  _usage;
            fi

        done <<END
$DATA
END

    #### 単発コマンドの場合
    else

        TYPE=`_get_type "$PARAM"`   #### $PARAM : 10.10.30.11 or 10.10.30.[1-254]

        if   [ "$TYPE" = "IP" ];    then  _get_ip_list "$PARAM";         #### IP
        elif [ "$TYPE" = "FQDN" ];  then  _get_fqdn_list "$PARAM";       #### FQDN
        else  _usage;
        fi

    fi
}

##############
#### Ping ####
##############
function _ping_maru()
{
    local RES=""
    local PRINT_ONLY="false"
    readonly IDX_E=`expr ${#_IP_FQDN[@]} - 1`

    [ $# -eq 1 -a "$1" = "-l" ] && PRINT_ONLY="true"

    [ -z "$IDX_E" -o $IDX_E -lt 0 ] && _usage

    for i in `seq 0 1 $IDX_E`
    do

        #### Ping を実行せず、リストのみ出力する場合
        if [ "$PRINT_ONLY" = "true" ]; then
            echo "${_IP_FQDN[$i]}        ${_COMMENT[$i]}"
            continue
        fi

        #### Ping を実行する場合
        #### 試行回数分 Ping
        RES=""

        for tmp in `seq 1 1 $PING_RETRY`
        do
            ping -c 1 ${_IP_FQDN[$i]} > /dev/null 2>&1

            if [ $? -eq 0 ]; then
                RES="○"
                break
            else
                RES="×"
            fi
        done

        #### 結果出力
        if [ -z "${_COMMENT[$i]}" ]; then
            echo "${_IP_FQDN[$i]} : $RES"
        else
            echo "${_IP_FQDN[$i]} : $RES        ${_COMMENT[$i]}"
        fi

    done
}

##############
#### Main ####
##############
if [ $# -eq 1 ]; then
    _get_list "$1"     #### リスト取得
    _ping_maru         #### Ping

elif [ $# -eq 2 ]; then
    _get_list "$1"     #### リスト取得
    _ping_maru "$2"    #### リスト出力 : -l のみ有効

else
    _usage
fi

echo "done."

IP / FQDN に対して Range Ping

オプションとして、-l (list) を付けると実際に ping は実行せず、リストだけ表示されるので、実際に実行する前の事前確認用として使えます。

IP に対して範囲指定 Ping (プレビュー)
1
2
3
4
5
# ./rping.sh 10.10.10.[11-13] -l
10.10.10.11
10.10.10.12
10.10.10.13
done.
IP に対して範囲指定 Ping
1
2
3
4
5
# ./rping.sh 10.10.10.[11-13]
10.10.10.11 : ○
10.10.10.12 : ○
10.10.10.13 : ○
done.
FQDN に対して範囲指定 Ping (プレビュー)
1
2
3
4
5
# ./rping.sh example-[01-03].com -l
example-01.com
example-02.com
example-03.com
done.
FQDN に対して範囲指定 Ping
1
2
3
4
5
# ./rping.sh example-[01-03].com
example-01.com : ○
example-02.com : ○
example-03.com : ○
done.

リスト化したファイルを指定する場合

ファイルに IP / FQDN リストを書いて、スクリプト実行時に そのファイルを指定することも可能です。

コメントを入れる場合は、# を付けてください。 間の空白は、Space でも Tab でも構いません。

list.txt
1
2
3
4
5
6
7
8
9
10.20.4.104        #### Server 104
10.20.4.105        #### Server 106
10.20.4.106        #### Server 106

10.20.4.[111-112]
#10.20.4.[200-210]     #### コメントアウトできます

example-[01-03].com    #### 検証用ドメイン 01-03
10.10.30.[251-253]     #### オフィス
リストファイルを指定して Range Ping
01
02
03
04
05
06
07
08
09
10
11
12
13
# ./rping list.txt
10.20.4.104 : ○        #### Server 104
10.20.4.105 : ○        #### Server 105
10.20.4.106 : ○        #### Server 106
10.20.4.111 : ○
10.20.4.112 : ○
example-01.com : ○        #### 検証用ドメイン 01-03
example-02.com : ○        #### 検証用ドメイン 01-03
example-03.com : ○        #### 検証用ドメイン 01-03
10.10.30.251 : ×        #### オフィス
10.10.30.252 : ×        #### オフィス
10.10.30.253 : ×        #### オフィス
done.

複数リストの組み合わせで実行する場合

使い方次第ですが、ファイル指定の場合には、rping.sh には一つしか指定できないようになっているので、複数のリストを使いたい場合は、以下のように。

list_1.txt
1
10.10.10.[11-13]      #### 拠点 A
list_2.txt
1
10.10.20.[11-13]      #### 拠点 B
test.sh
1
2
3
4
5
6
7
8
#!/bin/bash

FILES="/hoge/list_1.txt /hoge/list_2.txt"

for i in $FILES
do
    /usr/local/bin/rping.sh $i
done
実行結果
1
2
3
4
5
6
7
8
9
# ./test.sh
10.10.10.11 : ○      #### 拠点 A
10.10.10.12 : ○      #### 拠点 A
10.10.10.13 : ○      #### 拠点 A
done.
10.10.20.11 : ○      #### 拠点 B
10.10.20.12 : ○      #### 拠点 B
10.10.20.13 : ○      #### 拠点 B
done.

終わりに

念のためですが、Broadcast への Ping は止めた方が良いです。

CIDR を指定すると自動的に ping するようにしようと思いましたが、そこまでやる必要はないと思ったので実装していません。

○ と × が IP / FQDN の後に出力されるようになっているのですが、先頭に付けた方が見やすいかもしれないので、そこは必要に応じて修正してください。

一応、パラメータに渡された IP / IP Range と FQDN / FQDN Range に対して、最低限の正常性チェックは実施していますが。

色々細かいチェック処理を入れると逆に分かりづらくなってしまうことと、認められない形式のパラメータについては ping コマンドがエラーを吐いてくれるので、細かいチェックは、ping にお任せする感じです。

また、PING_RETRY に関しては、ping 応答がない端末へ ping -c1 を打つと 10秒間 (10000ms) トライしてくれるので、1 のまま使っても良いかもしれませんが、細かいオプション調整が必要な場合は line: 355 の ping オプションに追加してください。

応答しないサーバへ Ping した場合
1
2
3
4
5
# ping -c1 ping-ng.example.com
PING ping-ng (10.10.10.15) 56(84) bytes of data.

--- ping-ng.example.com ping statistics ---
1 packets transmitted, 0 received, 100% packet loss, time 10000ms

× になったらメール飛ばしたり (実装はしてないですが、NG 判定してる箇所に mail コマンド入れるだけです)、Ping サーバとかでも普通に使えると思います。

一定間隔で実行したい場合は、普通に cron 回してゴニョゴニョ。

以上、シェルスクリプト IP / FQDN 範囲指定 Ping でした。