我有一个名为 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 地址始终是该文件的第二个字段,您可以使用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
请参阅第一个问题Bash 常见问题解答:
while read -r _ ip; do printf "%s\n" "${ip[@]}"; done < abd
128.206.6.136
128.206.6.137
23.234.22.106