Conditionally replacing line contents
I answer a lot of Unix shell scripting questions for friends (I know, sad isn't it) but as I haven't written a blog article for a while I thought I document my thoughts in case the rest of world needs something similar.
Question
Have a file of IP addresses, bit like this:
192.168.20.0/24 192.167.20.254 10.0.2.254 216.200.119.0/16
basically if the line doesn't have a /
in it, I want to add a /32
at the end, can you do this in vi at all ?
I've been titting around with grep -v to do an inverse search, but can't get sed to adjust the file in place etc. Hence I think VI might be better.
Answer
There are many solutions to this kind of problem. Let's assume that the network addresses are contained in a file called file.txt
.
If the order was not important then a really simple solution would be this:
grep -v "/" file.txt | sed 's>$>/32>' > out.txt
grep "/" file.txt >> out.txt
- Filter
file.txt
and only give back the lines without a "/" in them. For each of those replace the end with "/32" and put the contents in the fileout.txt
. - Line 2 filters the
file.txt
, giving back the lines with a "/" in them and then append those lines to the fileout.txt
.
You will loses the order as doctored lines will appear at the top and undoctored lines will appear at the bottom. If the values were sorted you could just sort out.txt
at the end and restore the order (eg sort out.txt > out.sorted.txt
).
The trouble with editing the file "in place" is that there is a condition rule for each line. I'm not sure you could do it using vi
or ed
because you are not dealing with a range of lines in a buffer which is generally how those applications work.
I typically solve this kind of problem with a bit of awk
. Awk was one of the precursors to Perl but I prefer it. Perl just became a monolithic mess of difficult to remember syntax for no real benefit over what awk
already did.
awk '{ if ( $0 ~ /\// ) print $0; else print $0 "/32" }' < file.txt > out.txt
- Take the
file.txt
as input, if the line contains a "/" print the line untouched else print the line with "/32" on the end. Do this for each line and put the output inout.txt
.
The condition (in the if
) is a regular expression match so that everything within the // is used in the match, in our case we are looking for a "/" so we must delimited it with a backslash \/.
There a little awk
tutorial here that you might find useful:
http://www.grymoire.com/Unix/Awk.html
No feedback yet
Form is loading...