以下に示す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
ここで何が悪いのか分かりません。任意の助けいただければ幸いです。
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
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
と同様に動作するはずです。
そのために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
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