以下のように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
ここで何が間違っているのかわかりません。どんな助けでも大変感謝します。
答え1
最初はほぼ当たった。答えは特定のケースに適用されますが、エラーが発生するのは、変数ではなくファイルを検索するためにawk
使用しようとするためです。grep
また、正規表現を使用するときは常にgrep -E
安全に使用してください。また、バックティックは使用されなくなりました$()
。
grep
サポートされているシェルで変数を使用する正しい方法ここにある文字列次の3つは入力リダイレクトを使用しているため、<
コマンドgrep
($ip
変数)は実際には次のようになります。
ip="$(grep -oE '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}' <<< "$line")"
while
ファイルを検索する場合は1行ずつ移動することが保証されるため、常にループを使用しますが、for
奇妙な間隔がある場合は通常ループが発生します。また、cat
入力リダイレクトに代わる役に立たない使用も実装しました。この試み:
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 < "abd"
また、使用しているOSやバージョンが何であるかはわかりませんが、grep
過去にこのコマンドを使用するたびに中括弧の前のエスケープ文字は通常必要ありません。引用符なしでバックティックなしで使用または使用することによって発生する可能性がありますgrep -E
。わかりません。それを使用または使用せずに試してみて、何が起こっているのかを確認できます。
for
ループを使用するのかwhile
ループを使用するのかは、特定の状況にどのループが適しているのか、そして実行時間が最も重要であるかによって異なります。 OPは、各IPアドレスに別々の変数を割り当てようとするのではなく、ループ自体で使用できるように、行内の各IPアドレスに変数を割り当てたいと思います。この場合、$ip
繰り返しごとに1つの変数が必要です。私はこの問題についてしっかりとした立場に立っています。
答え2
IPアドレスが常にファイルの2番目のフィールドである場合、またはawk
をcut
使用して抽出できます。
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
答え3
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
元のコードは、${1:?}
ハードコードされた "abd"の代わりにファイル名をコマンドライン引数()として使用するように変更されました。
$ 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
$
上記のこの回答の例は、1/4秒以内に1000行のファイルを処理します。それでは、許可された回答の例がどのように実行されるかを見てみましょう。
$ 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
$
まあ。許可された回答の例は約3 1/2秒で実行されます。169倍遅いこの回答例では、1/40秒以上です。
もう少し頑張って5000行でテストしてみましょう。
$ time sh ip-example.sh abd.5000 > /dev/null
real 0m0.052s
user 0m0.051s
sys 0m0.029s
~について二重ただ処理する5倍のデータ。
$ time bash accepted.sh abd.5000 > /dev/null
real 0m17.561s
user 0m3.817s
sys 0m14.333s
許容される回答のサンプルコードにはほとんど5倍長くなる1000行のデータより5倍多くのデータを処理しました。
結論として
許容される回答の例は次のとおりです。337倍長くなるこの回答のコードの代わりに5000行のファイルを処理してくださいip-example.sh
(このページの他の回答も同様のことをする必要がありますip-example.h
)。
答え4
最初の質問を見るバッシュFAQ:
while read -r _ ip; do printf "%s\n" "${ip[@]}"; done < abd
128.206.6.136
128.206.6.137
23.234.22.106