I have a text file named abd shown below.
48878 128.206.6.136 34782 128.206.6.137 12817 23.234.22.106
I want to extract only IP address from the text and store it in a variable and use for other purpose.
I have tried this.
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
I am getting an error as follows
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
I don’t know what is going wrong here.
Any help would be appreciated.
Answers:
Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.
Method 1
You almost had it right the first time. The awk answer is good for your specific case, but the reason you were receiving an error is because you were trying to use grep as if it were searching for a file instead of a variable.
Also, when using regular expressions, I always use grep -E just to be safe. I have also heard that backticks are deprecated and should be replaced with $().
The correct way to grep a variable with on shells that support herestrings is using input redirection with 3 of these guys: <, so your grep command ($ip variable) should actually read as follows:
ip="$(grep -oE '[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}' <<< "$line")"
If it is a file you are searching, I always use a while loop, since it is guaranteed to go line-by-line, whereas for loops often get thrown off if there is any weird spacing. You are also implementing a useless use of cat which could be replace by input redirection as well. Try this:
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"
Also, I don’t know what OS or version of grep you are using, but the escape character you had before the curly braces is usually not required whenever I have used this command in the past. It could be from using grep -E or because I use it in quotes and without backticks — I don’t know. You can try it with or without and just see what happens.
Whether you use a for loop or a while loop, that is based on which one works for you in your specific situation and if execution time is of utmost importance. It doesn’t appear to me as if OP is trying to assign separate variables to each IP address, but that he wants to assign a variable to each IP address within the line so that he can use it within the loop itself — in which case he only needs a single $ip variable per iteration. I’m sticking to my guns on this one.
Method 2
If the IP address is always the second field of that file, you can use awk or cut to extract it.
awk '{print $2}' abd
or
cut -d' ' -f2 abd
If you need to iterate through the IP addresses, the usual for or while loops can be used. For example:
for ip in $(cut -d' ' -f2 abd) ; do ... ; done
or
awk '{print $2}' abd | while read ip ; do ... ; done
Or you can read all the IP addresses into an array:
$ IPAddresses=($(awk '{print $2}' abd))
$ echo "${IPAddresses[@]}"
128.206.6.136 128.206.6.137 23.234.22.106
Method 3
grep searches files or standard input for the patterns. You cannot pass data strings to match on the grep command line. Try this:
grep -o '[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}' abd
If you need to get each IP address in a variable:
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
Comparative Performance Testing of the accepted answer
The answer recommends executing a separate invocation of grep on each line of the input file. Let’s see how that works out with files of 1000 to 5000 lines. The files abd.1000 and abd.5000 were created by simply replicating the original example file in the question. The original code was changed only to take the filename as a command line argument (${1:?}) instead of the hardcoded “abd”.
$ wc -l abd.1000 abd.5000 1000 abd.1000 5000 abd.5000 6000 total
Test the example code in this answer on a 1000 line file:
$ 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
$
The above shows that the example in this answer processed a 1000 line file in less than 1/4 second. Now let’s see how the example in the accepted answer performs:
$ 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
$
Hmmm. The example in the accepted answer executes in 3 1/2 seconds, about 169 times slower than the 1/40 second in example for this answer.
Let’s up the ante and test with 5000 lines:
$ time sh ip-example.sh abd.5000 > /dev/null real 0m0.052s user 0m0.051s sys 0m0.029s
About twice as long to process 5 times more data.
$ time bash accepted.sh abd.5000 > /dev/null real 0m17.561s user 0m3.817s sys 0m14.333s
The example code in the accepted answer takes almost 5 times as long to process 5 times more data than to process 1000 lines of data.
Conclusions
The example in the accepted answer takes 337 times longer to process a 5000 line file than the ip-example.sh code in this answer (the other answers on this page should perform similarly to ip-example.h).
Method 4
I suggest you use AWK for that purpose. It’s much more appropriate tool for processing columns.
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
Method 5
See the first question in the Bash FAQ:
while read -r _ ip; do printf "%sn" "${ip[@]}"; done < abd
128.206.6.136
128.206.6.137
23.234.22.106
All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0