Bash:IPv4アドレスの4つのセクションの1つを抽出します


9

構文${var##pattern}を使用して${var%%pattern}、IPv4アドレスの最後と最初のセクションを抽出できます。

IP=109.96.77.15
echo IP: $IP
echo 'Extract the first section using ${var%%pattern}: ' ${IP%%.*}
echo 'Extract the last section using ${var##pattern}: ' ${IP##*.}

パラメータ拡張を使用してIPv4アドレスの2番目または3番目のセクションを抽出するにはどうすればよいですか?

これが私の解決策です:配列を使用してIFS変数を変更します。

:~/bin$ IP=109.96.77.15
:~/bin$ IFS=. read -a ArrIP<<<"$IP"
:~/bin$ echo ${ArrIP[1]}
    96
:~/bin$ printf "%s\n" "${ArrIP[@]}"
    109
    96
    77
    15

また、私は使用して、いくつかのソリューションを書かれているawksedcutのコマンドを。

さて、私の質問は:配列とIFS変更を使用しないパラメーター拡張に基づくより簡単な解決策はありますか?


1
あなただけ設定する必要がありますIFSのためにreadそこに:IFS=. read -a ArrIP<<<"$IP"
muru

1
少なくとも複数の変数を使用しない限り、bashにはありません。単一のパラメーター展開では、2番目または3番目のコンポーネントを取得できません。Zshはパラメーター展開をネストできるため、そこで可能かもしれません。
muru、

@muru Zshソリューションを提供していただけますか?
sci9 2018年

4
常にIP v4アドレスを処理し、IP v6アドレスを決して持たないという保証はありますか?
Mawgはモニカを2018

4
IFS=. read a b c d <<< "$IP"受け入れられない理由がありますか(Bashを使用している場合、それはそうです)?なぜそれはパラメータ拡張で行わなければならないのですか?
ilkkachu

回答:


16

IFSのデフォルト値を想定して、各オクテットを独自の変数に抽出します。

read A B C D <<<"${IP//./ }"

または、次のように配列に入れます:

A=(${IP//./ })

2
+1。これは、OPの制限を順守する最も単純で最も簡単な方法であると私には思われます。
デジタルトラウマ

7

あなたの問題文は、あなたが意図したよりも少し寛大かもしれません。抜け穴を悪用する危険があるとして、ここに示唆された解決策があります:

first=${IP%%.*}
last3=${IP#*.}
second=${last3%%.*}
last2=${last3#*.}
third=${last2%.*}
fourth=${last2#*.}
echo "$IP -> $first, $second, $third, $fourth"

これはやや不格好です。2つの使い捨て変数が定義されており、より多くのセクション(MACまたはIPv6アドレスなど)を処理するように容易に調整されていません。  Sergiy Kolodyazhnyyの答えは、これを上記に一般化するように私に刺激を与えました。

slice="$IP"
count=1
while [ "$count" -le 4 ]
do
    declare sec"$count"="${slice%%.*}"
    slice="${slice#*.}"
    count=$((count+1))
done

このセットsec1sec2sec3sec4して検証することができ、

printf 'Section 1: %s\n' "$sec1"
printf 'Section 2: %s\n' "$sec2"
printf 'Section 3: %s\n' "$sec3"
printf 'Section 4: %s\n' "$sec4"
  • whileループが理解しやすいべきである-それを4回繰り返します。
  • 私の最初のソリューション(上記)sliceの代わりにlast3、セルギが変数の名前として選択しましたlast2
  • declare sec"$count"="value"割り当てる方法でありsec1sec2sec3sec4 する場合count1234。少し似てevalいますが、より安全です。
  • value"${slice%%.*}"、の値が私の元の答えの割り当てに類似しておりfirstsecondそしてthird

6

一時的に再定義されIFSなかったソリューションを具体的に要求したことは承知していますが、ここでは説明しなかった甘くてシンプルなソリューションがあるので、ここに示します。

IFS=. ; set -- $IP

その短いコマンドは、シェルの中にあなたのIPアドレスの要素を置く位置パラメータ $1$2$3$4。ただし、おそらく最初にオリジナルIFSを保存し、後でそれを復元する必要があります。

知るか?多分あなたはその簡潔さと効率のためにこの答えを再考し、受け入れるでしょう。

(これは以前は誤ってとして与えられていましたIFS=. set -- $IP


5
私はそれがうまくいくとは思いません:IFS同じコマンドラインで変更した場合、同じコマンドラインの変数が展開されても新しい値は有効になりません。と同じx=1; x=2 echo $x
ilkkachu

6
@chepner set$IFS、まったく使用していません。$IFS単語の分割にのみ使用され$IPますが、ここでは割り当てが遅すぎるため、効果がありません。この答えは基本的に間違っています。IP=109.96.77.15 bash -c 'IFS=. set -- $IP; echo "$2"'POSIXモードかどうかにかかわらず、何も出力しません。あなたは必要と思いIFS=. command eval 'set -- $IP'、あるいはIFS=. read a b c d << "$IP"
ステファンChazelasに

4
.以前のテストの1つでIFSをに設定していたので、おそらくうまくいきます。実行するIP=1.2.3.4 bash -xc 'IFS=. set $IP; echo "$2"'と、機能しないことがわかります。そして、IP=1.2.3.4 bash -o posix -xc 'IFS=. set $IP; echo "\$1=$1 \$2=$2 IFS=$IFS"'@ chepnerのポイントを説明するために参照してください。
ステファンChazelas

2
急いで無駄にしました、答えは明らかに間違っています、そしてそれは動作するように修正する必要があります。
Lizardx

3
@ user1404316、テストに使用したコマンドの正確なセットを投稿できますか?(おそらく、シェルのバージョンです。)回答に書かれているように動作しないというコメントで、他に4人のユーザーが言っています。例付き。
ilkkachu

5

最も簡単ではありませんが、次のようなことができます:

$ IP=109.96.77.15
$ echo "$((${-+"(${IP//./"+256*("}))))"}&255))"
109
$ echo "$((${-+"(${IP//./"+256*("}))))"}>>8&255))"
96
$ echo "$((${-+"(${IP//./"+256*("}))))"}>>16&255))"
77
$ echo "$((${-+"(${IP//./"+256*("}))))"}>>24&255))"
15

(そのところはは、ksh93で動作するはず${var//pattern/replacement}オペレータがから来ている)、bash4.3以降、busyboxのshyashmkshそしてzsh、もちろんかかわらzsh、はるかに単純なアプローチがあります。古いバージョンのではbash、内部の引用符を削除する必要があります。他のほとんどのシェルで削除された内部引用符でも機能しますが、ksh93では機能しません。

これは$IP、IPv4アドレスの有効な4進数10進数表現が含まれていることを前提としています(ただし、これは0x6d.0x60.0x4d.0xf(および一部のシェルでは8進数でも)などの4進数16進数表現でも機能しますが、値は10進数で出力されます)。のコンテンツが$IP信頼できないソースからのものである場合、それはコマンドインジェクションの脆弱性に相当します。

基本的に、我々はすべてのを交換しているよう.$IP+256*(、私たちは評価してしまいます。

 $(( (109+256*(96+256*(77+256*(15))))>> x &255 ))

したがって、IPv4アドレスのように、これらの4バイトから32ビット整数を構築しています(最終的にはバイトが逆になります)。次に>>&ビットごとの演算子を使用して関連するバイトを抽出します。

それ以外の場合、算術パーサーは括弧の不一致について文句を言うため、${param+value}標準の演算子(ここ$-では常に設定されることが保証されています)を使用しvalueます。ここでシェルは終了見つけることができる))開口部のために$((、そしてその後、その内部の拡張は評価する算術式になります行います。

$(((${IP//./"+256*("}))))&255))代わりに、シェルは、第2および第3の治療でしょう)終値としてそこ秒))のため$((、構文エラーを報告します。

ksh93では、次のことも実行できます。

$ echo "${IP/@(*).@(*).@(*).@(*)/\2}"
96

bashmkshzsh は、ksh93のコピーした${var/pattern/replacement}オペレータをしかし、キャプチャー・グループは、一部を処理していないこと。zsh異なる構文でサポートします:

$ setopt extendedglob # for (#b)
$ echo ${IP/(#b)(*).(*).(*).(*)/$match[2]}'
96

bashは、正規表現一致演算子ある形式のキャプチャグループ処理をサポートしていますが、ではサポートしていません${var/pattern/replacement}

POSIXly、あなたは使うでしょう:

(IFS=.; set -o noglob; set -- $IP; printf '%s\n' "$2")

のようなnoglob値の予期せぬ事態を避けるために、サブシェルはこれらの変更の範囲をオプションとに制限します。$IP10.*.*.*$IFS


IPv IPv4アドレスは32ビット整数にすぎず、たとえば127.0.0.1は多くの(最も一般的な)テキスト表現の1つにすぎません。ループバックインターフェースの同じ典型的なIPv4アドレスは、0x7f000001または127.1(127.0 1/ 8クラスAネットワーク上のアドレスであるとここではより適切なものかもしれません)、または0177.0.1、または1の他の組み合わせとして表すこともできます。8進数、10進数、または16進数として表される4つの数値。pingたとえば、これらすべてを渡すことができ、それらがすべてlocalhostにpingすることがわかります。

or またはor で任意の一時変数(ここでは$n)を設定することの副作用を気にしない場合は、次のコマンドを使用して、これらすべての表現を32ビット整数に戻すことができます。bashksh93zsh -o octalzeroeslksh -o posix

$((n=32,(${IP//./"<<(n-=8))+("})))

次に、上記のような>>/の&組み合わせですべてのコンポーネントを抽出します。

$ IP=0x7f000001
$ echo "$((n=32,(${IP//./"<<(n-=8))+("})))"
2130706433
$ IP=127.1
$ echo "$((n=32,(${IP//./"<<(n-=8))+("})))"
2130706433
$ echo "$((n=32,((${IP//./"<<(n-=8))+("}))>>24&255))"
127
$ perl -MSocket -le 'print unpack("L>", inet_aton("127.0.0.1"))'
2130706433

mkshは、算術式に符号付き32ビット整数を使用します。$((# n=32,...))そこで、符号なし32ビット数を強制的に使用することができます(およびposix8進定数を認識するためのオプション)。


大きなコンセプトは理解していますが、今まで見たことがありません${-+。それに関するドキュメントも見つかりません。動作しますが、確認したいのですが、文字列を数式に変換するだけですか?正式な定義はどこにありますか?また、パラメーター拡張置換セクション内の余分な引用は、GNU bashバージョン4.1.2(2)リリースのCentOS 6.6では機能しません。私は代わりにこれをしなければなりませんecho "$((${-+"(${IP//./+256*(}))))"}>>16&255))"
でした

1
@LeviUzodike、編集を参照してください。
ステファンChazelas

4

zshを使用すると、パラメーター置換をネストできます。

$ ip=12.34.56.78
$ echo ${${ip%.*}##*.}
56
$ echo ${${ip#*.}%%.*}
34

これはbashでは不可能です。


1
ではzsh、あなたが好むかもしれない${${(s(.))ip}[3]}
ステファンChazelas

4

かしこまりました。象のゲームをしてみましょう。

$ ipsplit() { local IFS=.; ip=(.$*); }
$ ipsplit 10.1.2.3
$ echo ${ip[1]}
10

または

$ ipsplit() { local IFS=.; echo $*; }
$ set -- `ipsplit 10.1.2.3`
$ echo $1
10

2
「象ゲーム」とは?
ワイルドカード

1
@ワイルドカードのような楕円形の駄洒落/冗談、それは象の物語を説明する盲目の人々への参照であり、すべての人が自分の見解を持つつもりである、そして望んでいた王の古代の慣習を見るための部分があるかもしれません何世紀にもわたって、娯楽価値のために贈り物にされる傾向がある単に怪しげな価値の贈り物に柔らかくなった破滅的に高価な維持費を伴う贈り物を..両方ともここに適用されるよう
でし

3

IP=12.34.56.78

IFS=. read a b c d <<<"$IP"

そして

#!/bin/bash
IP=$1

regex="(${IP//\./)\.(})"
[[ $IP =~ $regex ]]
echo "${BASH_REMATCH[@]:1}"

説明:

パラメータ展開${IP// }を使用して、IPの各ドットを開始括弧、ドット、終了括弧に変換します。最初の括弧と閉じ括弧を追加すると、次のようになります。

regex=(12)\.(34)\.(56)\.(78)

これにより、テスト構文で正規表現一致の4つのキャプチャ括弧が作成されます。

[[ $IP =~ $regex ]]

これにより、最初のコンポーネントなしで配列BASH_REMATCHを出力できます(正規表現全体が一致します)。

echo "${BASH_REMATCH[@]:1}"

括弧の量は、一致した文字列に合わせて自動的に調整されます。したがって、これは、長さが異なるにもかかわらず、IPv6アドレスのMACまたはEUI-64のいずれかに一致します。

#!/bin/bash
IP=$1

regex="(${IP//:/):(})"
[[ $IP =~ $regex ]]
echo "${BASH_REMATCH[@]:1}"

それを使う:

$ ./script 00:0C:29:0C:47:D5
00 0C 29 0C 47 D5

$ ./script 00:0C:29:FF:FE:0C:47:D5
00 0C 29 FF FE 0C 47 D5

3

以下/bin/shは、POSIX (私の場合はdash)で行われる小さなソリューションです。パラメーター展開(IFSここではありません)を繰り返し使用する関数と名前付きパイプで、Stephaneの回答でnoglob述べた理由によるオプションが含まれています。

#!/bin/sh
set -o noglob
get_ip_sections(){
    slice="$1"
    count=0
    while [ -n "${slice}" ] && [ "$count" -ne 4 ]
    do
        num="${slice%%.*}"
        printf '%s ' "${num}"
        slice="${slice#*${num}.}"
        count=$((count+1))
    done
}

ip="109.96.77.15"
named_pipe="/tmp/ip_stuff.fifo"
mkfifo "${named_pipe}"
get_ip_sections "$ip" > "${named_pipe}" &
read sec1 sec2 sec3 sec4 < "${named_pipe}"
printf 'Actual ip:%s\n' "${ip}"
printf 'Section 1:%s\n' "${sec1}"
printf 'Section 3:%s\n' "${sec3}"
rm  "${named_pipe}"

これは次のように機能します:

$ ./get_ip_sections.sh 
Actual ip:109.96.77.15
Section 1:109
Section 3:77

そしてにip変更109.*.*.*

$ ./get_ip_sections.sh 
Actual ip:109.*.*.*
Section 1:109
Section 3:*

4反復のループ保持カウンターは、有効なIPv4アドレスの4セクションを占めますが、名前付きパイプを使用したアクロバットでは、ループのサブシェルで変数がスタックするのではなく、スクリプト内でIPアドレスのセクションをさらに使用する必要があります。


パイプを使用しないように回答を変更し、既存の回答に追加しました。
G-Manは 'Reinstate Monica'を

0

awkで単純なソリューションを使用しないのはなぜですか?

$ IP="192.168.1.1" $ echo $IP | awk -F '.' '{ print $1" "$2" "$3" "$4;}'

結果 $ 192 168 1 1


弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.