比较性能测试接受的答案

比较性能测试接受的答案

我有一个名为 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在支持的 shell 上使用变量的正确方法这里的字符串正在与其中 3 个家伙使用输入重定向:<,因此您的grep命令($ip变量)实际上应如下所示:

ip="$(grep -oE '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}' <<< "$line")"

如果您正在搜索一个文件,我总是使用循环while,因为它可以保证逐行进行,而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"

另外,我不知道grep您使用的是什么操作系统或版本,但是每当我过去使用此命令时,通常不需要大括号之前的转义字符。它可能来自使用grep -E或因为我在引号中使用它并且没有反引号 - 我不知道。您可以尝试使用或不使用它,看看会发生什么。

无论您使用for循环还是while循环,都取决于哪种循环适合您的具体情况以及执行时间是否最重要。在我看来,OP 并不是试图为每个 IP 地址分配单独的变量,而是他想为该行中的每个 IP 地址分配一个变量,以便他可以在循环本身中使用它 - 其中在这种情况下,他每次迭代只需要一个$ip变量。我在这件事上坚持己见。

答案2

如果 IP 地址始终是该文件的第二个字段,您可以使用awkcut来提取它。

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.1000abd.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

请参阅第一个问题Bash 常见问题解答:

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

相关内容