web-dev-qa-db-ja.com

テキストからIPアドレスを抽出して変数に格納する

以下に示すabdという名前のテキストファイルがあります。

48878 128.206.6.136
34782 128.206.6.137
12817 23.234.22.106

テキストからIPアドレスのみを抽出して変数に格納し、他の目的で使用したい。

私はこれを試しました。

for line in `cat abd`
do

ip=`grep -o '[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}' $line`

echo $ip

done

次のようなエラーが発生します

grep: 34782: No such file or directory

grep: 128.206.6.137: No such file or directory

grep: 12817: No such file or directory

grep: 23.234.22.106: No such file or directory

ここで何が悪いのか分かりません。任意の助けいただければ幸いです。

5
Swatesh Pakhare

IPアドレスが常にそのファイルの2番目のフィールドである場合は、awkまたはcutを使用してIPアドレスを抽出できます。

awk '{print $2}' abd

または

cut -d' ' -f2 abd

IPアドレスを反復処理する必要がある場合は、通常のforまたはwhileループを使用できます。例えば:

for ip in $(cut -d' ' -f2 abd) ; do ... ; done

または

awk '{print $2}' abd | while read ip ; do ... ; done

または、すべてのIPアドレスを配列に読み込むことができます。

$ IPAddresses=($(awk '{print $2}' abd))
$ echo "${IPAddresses[@]}"
128.206.6.136 128.206.6.137 23.234.22.106
9
cas

grepは、ファイルまたは標準入力からパターンを検索します。 grepコマンドラインで一致するデータ文字列を渡すことはできません。これを試して:

grep -o '[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}' abd

変数の各IPアドレスを取得する必要がある場合:

grep -o '[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}' abd |
while read IP
do
    echo "$IP"
done

承認された回答 の比較パフォーマンステスト

答えは、入力ファイルの各行でgrepの個別の呼び出しを実行することを推奨しています。 1000行から5000行のファイルでどのように機能するかを見てみましょう。ファイルabd.1000およびabd.5000は、質問の元のサンプルファイルを単に複製することによって作成されました。元のコードは、ハードコードされた「abd」ではなく、ファイル名をコマンドライン引数(${1:?})として受け取るようにのみ変更されました。

$ wc -l abd.1000 abd.5000
  1000 abd.1000
  5000 abd.5000
  6000 total

この回答のサンプルコードを1000行のファイルでテストします。

$ cat ip-example.sh
#!/bin/sh
grep -o '[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}' "${1:?}" |
while read IP
do
    echo "$IP"
done

$ time sh ip-example.sh abd.1000 > /dev/null

real    0m0.021s
user    0m0.007s
sys     0m0.017s
$

上記は、この回答の例が1000行のファイルを1/4秒未満で処理したことを示しています。次に、受け入れられた回答の例がどのように実行されるかを見てみましょう。

$ cat accepted.sh
#!/bin/bash
while read line; do
  ip="$(grep -oE '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}' <<< "$line")"
  echo "$ip"
done < "${1:?}"

$ time bash accepted.sh abd.1000 > /dev/null

real    0m3.565s
user    0m0.739s
sys     0m2.936s
$

うーん。受け入れられた回答の例は、この回答の例の1/40秒よりも169倍遅いで3 1/2秒で実行されます。

アンティを上げて5000行でテストしてみましょう。

$ time sh ip-example.sh abd.5000 > /dev/null

real    0m0.052s
user    0m0.051s
sys     0m0.029s

2倍処理にかかる時間5倍のデータ

$ time bash accepted.sh abd.5000 > /dev/null

real    0m17.561s
user    0m3.817s
sys     0m14.333s

受け入れられた回答のサンプルコードでは、1000行のデータを処理するよりも5倍多くのデータを処理するために、ほぼ5倍の時間が必要です。

結論

受け入れられた回答の例では、この回答のip-example.shコードよりも5000行のファイルの処理に337倍長い時間が必要です(これに関する他の回答)ページはip-example.hと同様に動作するはずです。

6
RobertL

そのためにAWKを使用することをお勧めします。カラムを処理するためのより適切なツールです。

xieerqi:$ vi ipAddresses

xieerqi:$ awk '{printf $2" "}' ipAddresses                                     
128.206.6.136 128.206.6.137 23.234.22.106 
xieerqi:$ ARRAY=($(awk '{printf $2" "}' ipAddresses))                          

xieerqi:$ echo ${ARRAY[@]}
128.206.6.136 128.206.6.137 23.234.22.106

xieerqi:$ echo ${ARRAY[1]} ${ARRAY[2]}
128.206.6.137 23.234.22.106

xieerqi:$ cat ipAddresses                                                      
48878 128.206.6.136
34782 128.206.6.137
12817 23.234.22.106
4

Bash FAQ の最初の質問を参照してください:

while read -r _ ip; do printf "%s\n" "${ip[@]}"; done < abd
128.206.6.136
128.206.6.137
23.234.22.106
3
jasonwryan