Writing to /etc/networking/interfaces at boot using sed/awk? - bash

Newbie here,
I'm trying to write to an auto-generated /etc/network/interfaces file of a newely provisioned XEN Ubuntu (12.04/10.04/8.04) DomU server at boot time using (currently) sed.
The auto-generated file is formatted as below:
auto eth0
iface eth0 inet static
address 192.168.0.88
gateway 192.168.0.254
network 255.255.255.255
auto lo
iface lo inet loopback
Using sed, I'm trying to alter lines 1 & 2, add a third line, remove the gateway and last two lines, and append four extra lines at the very end.
I'm currently stuck on adding the third line, as the script adds this line everytime it's run:
#!/bin/bash
sed -i "1s/.*/auto lo eth0/" /tmp/interfaces
sed -i "2s/.*/iface lo inet loopback/" /tmp/interfaces
sed -i "2a\iface eth0 inet static" /tmp/interfaces
sed -i "s/auto lo//g" /tmp/interfaces
Is it possible to add the third line only if it doesn't exist using sed (or awk)?
Likewise, how can I delete the gateway and last two lines only if they don't exist?
I'm new to sed, so am wondering whether I should be looking at awk instead for achieving this?

You can do that with sed:
sed -i -e '4{/iface eth0 inet static/! i\
iface eth0 inet static
}'
You can group commands with braces. The commands in the braces will only execute on the third line. The i insert command will only execute on the third line and if the third line doesn't match the string between slashes (the ! after it tells it to execute when it doesn't match).
You can do the same to delete:
sed -i -e '3{/gateway/d}'
Here we delete the third line only if it contains the string gateway. You could probably be more generic and simply do:
sed -i -e '/gateway/d'
which will delete all lines that contain gateway, but maybe that's not what you want.
As for deleting the last lines, the easiest solution would be:
sed -i -e '${/auto lo/d;/iface lo inet loopback/d}'
sed -i -e '${/auto lo/d;/iface lo inet loopback/d}'
Where the d delete command is executed on the last line if it matches either auto lo or iface lo inet loopback. Executing it twice will delete the last two lines if they match the patterns.
If you want to add lines to the end of the file, you can do:
sed -i -e '$a\
newline1\
newline2\
newline3'
Or maybe only add them if the last line isn't a specific line:
sed -i -e '${/192\.168\.1\.1/!a\
newline1\
newline2\
newline3
}'
Hope this helps a little =)

This script do the job: https://github.com/JoeKuan/Network-Interfaces-Script
awk -f changeInterface.awk <interfaces file> <device=ethX>
<mode=(dhcp|static|manual)> [action=add|remove]
[address=<ip addr> netmask=<ip addr> <name=value> ...]

Related

linux bash insert text at a variable line number in a file

I'm trying to temporarily disable dhcp on all connections in a computer using bash, so I need the process to be reversible. My approach is to comment out lines that contain BOOTPROTO=dhcp, and then insert a line below it with BOOTPROTO=none. I'm not sure of the correct syntax to make sed understand the line number stored in the $insertLine variable.
fileList=$(ls /etc/sysconfig/network-scripts | grep ^ifcfg)
path="/etc/sysconfig/network-scripts/"
for file in $fileList
do
echo "looking for dhcp entry in $file"
if [ $(cat $path$file | grep ^BOOTPROTO=dhcp) ]; then
echo "disabling dhcp in $file"
editLine=$(grep -n ^BOOTPROTO=dhcp /$path$file | cut -d : -f 1 )
#comment out the original dhcp value
sed -i "s/BOOTPROTO=dhcp/#BOOTPROTO=dhcp/g" $path$file
#insert a line below it with value of none.
((insertLine=$editLine+1))
sed "$($insertLine)iBOOTPROTO=none" $path$file
fi
done
Any help using sed or other stream editor greatly appreciated. I'm using RHEL 6.
The sed editor should be able to do the job, without having to to be combine bash, grep, cat, etc. Easier to test, and more reliable.
The whole scripts can be simplified to the below. It performs all operations (substitution and the insert) with a single pass using multiple sed scriptlets.
#! /bin/sh
for file in $(grep -l "^BOOTPROTO=dhcp" /etc/sysconfig/network-scripts/ifcfg*) ; do
sed -i -e "s/BOOTPROTO=dhcp/#BOOTPROTO=dhcp/g" -e "/BOOTPROTO=dhcp/i BOOTPROTO=none" $file
done
As side note consider NOT using path as variable to avoid possible confusion with the 'PATH` environment variable.
Writing it up, your attempt with the following fails:
sed "$($insertLine)iBOOTPROTO=none" $path$file
because:
$($insertLine) encloses $insertLIne in a command substitution which when $insertLIne is evaluated it returns a number which is not a command generating an error.
your call to sed does not include the -i option to edit the file $path$file in place.
You can correct the issues with:
sed -i "${insertLine}i BOOTPROTO=none" $path$file
Which is just sed - i (edit in place) and Ni where N is the number of the line to insert followed by the content to insert and finally what file to insert it in. You add ${..} to insertLine to protect the variable name from the i that follows and then the expression is double-quoted to allow variable expansion.
Let me know if you have any further questions.
(and see dash-o's answer for refactoring the whole thing to simply use sed to make the change without spawning 10 other subshells)

Fetch value after certain pattern in Unix

This is the text that I have
Peer Addr 1.1.1.1, Intf Port-Channel0.1, State Up
VRF default, LAddr 1.1.1.1, LD/RD 090/592
Session state is Up and using echo function with 300 ms interval
Detect Time: 8000
Sched Delay: 1*TxInt: 3522100, 2*TxInt: 5, 3*TxInt: 0, GT 3*TxInt: 0
Registered protocols: isis bgp ospf
I want the values after Peer Addr, Intf and Registered protocols
Expected output
1.1.1.1
Port-Channel0.1
isis bgp ospf
This is what I have tried
grep -oP "Peer Addr\s+\K\w+"
I am unable to get the required output. I am new to shell scripting and would be great if someone can help me out on this one. I don't want all the output in a single command. I would like to store these as three different variables
With GNU awk for multi-char RS:
$ awk -v RS='[\n,]\\s*' 'sub(/^(Peer Addr|Intf|Registered protocols:) /,"")' file
1.1.1.1
Port-Channel0.1
isis bgp ospf
With GNU grep:
grep -Po '(Peer Addr|Intf|protocols:) \K.*?(?=(,|$))' file
Output:
1.1.1.1
Port-Channel0.1
isis bgp ospf
-P PATTERN: Interpret PATTERN as a Perl regular expression.
-o: Print only the matched (non-empty) parts of a matching line, with each such part on a separate output line.
\K: If \K appears in a Perl regex, it causes the regex matcher to drop everything before that point.
(?=...): The captured match must be followed by , or end of line ($) but that part is dropped, too.
With sed, you could do something like this:
sed -rn 's/Peer Addr *([^,]+), */\1/;
s/Intf *([^,]+),.*/\n\1/;
s/Registered protocols: *//;
ta;d;:a p'
awk -F"[, ]" '/^Peer Addr/{print $3"\n"$6} /^Registered protocols:/{sub(/Registered protocols: /,"");print}' File
Here IFS as , and .
/^Peer Addr/{print $3"\n"$6} : If a line starts with Peer Addr print $3( Here 1.1.1.1) and $6( Port-Channel0.1).
/^Registered protocols:/{sub(/Registered protocols: /,"");print}': If a line starts with Registered protocols: print entire line after removing the string Registered protocols: with sub awk function.

sed: replace ip in hosts file, using hostname as pattern

I'm learning about sed but it is very difficult to me understand it.
I have adsl with dynamic ip so and i want to put current ip on hosts file.
This following script just tells me the current wan ip address and no more:
IP=$(dig +short myip.opendns.com #resolver1.opendns.com)
echo $IP
The result:
192.42.7.73
So, i have a line on hosts file with the old ip address:
190.42.44.22 peep.strudel.com
and i want to update host file like this:
192.42.7.73 peep.strudel.com
How can i do it? I think i can use the hostname as pattern...
The reason of doing this is because my server is a client of my router, so it access the internet thru its gateway and not directly. And postfix always is logging me that "connect from unknown [x.x.x.x]" (where x.x.x.x is my wan ip!) and it can't resolve that ip. I think that maybe if i specify this relating with my fqdn host/domain, on hosts file it will works better.
Thanks
Sergio.
You can use a simple shell script:
#! /bin/bash
IP=$(dig +short myip.opendns.com #resolver1.opendns.com)
HOST="peep.strudel.com"
sed -i "/$HOST/ s/.*/$IP\t$HOST/g" /etc/hosts
Explanation:
sed -i "/$HOST/ s/.*/$IP\t$HOST/g" /etc/hosts means in the line which contains $HOST replace everything .* by $IP tab $HOST.
using sed
sed -r "s/^ *[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+( +peep.strudel.com)/$IP\1/"
.
[0-9]+\. find all lines that matches 1 or more digits with this pattern 4 consecutive times then pattern peep.strudel.com .The parenthesis around the pattern peep.strudel.com save it as \1 then replace the whole patten with your variable and your new ip.
another approach:instead of saving pattern to a variable named IP, you can execute your command line inside sed command line to get the new IP .
sed -r "s/^ *[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+( +peep.strudel.com)/$(dig +short myip.opendns.com #resolver1.opendns.com)\1/"
using gawk
gawk -v IP=$IP '/ *[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+( +peep.strudel.com).*/{print gensub(/ *[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+( +peep.strudel.com)/,IP"\\1","g")}'
You need to include the sed code inside double quotes so that the used variable got expanded.
sed "s/\b\([0-9]\{1,3\}\.\)\{1,3\}[0-9]\{1,3\}\b/$IP/g" file
Add -i parameter to save the changes made. In basic sed \(..\) called capturing group. \{min,max\} called range quantifier.
Example:
$ IP='192.42.7.73'
$ echo '190.42.44.22 peep.strudel.com' | sed "s/\b\([0-9]\{1,3\}\.\)\{1,3\}[0-9]\{1,3\}\b/$IP/g"
192.42.7.73 peep.strudel.com

want to delete ith matching pattern from file

i want to delete the static entry from dhcpd.cong file by using shell command. Currently I am using sed for the hostname. But there are multiple static entries for same hostname. here is the portion from sample dhcpd.conf file:
host my-system4 {
hardware ethernet 11:22:33:44:55:66;
fixed-address 192.168.17.54;
}
host my-system4 {
hardware ethernet 22:33:44:55:66:77;
fixed-address 192.168.17.34;
}
Here i used:
sed -i '/host my-system4/,/^\}/d' xyz.txt
But this command is deleting all the host my-system4 entries from file. How can I delete the specific entry by doing grep on fixed-address. Also the number of lines under host my-system4 may also vary. I nee shell command which do grep on fixed-address and delete only that specific host entry.
You could say:
sed -e :a -e '/host my-system4/,/}/ { /}/!{ $!{N;ba};};/pattern/d;}' filename
to delete the entry containing the specified pattern.
For example,
sed -e :a -e '/host my-system4/,/}/ { /}/!{ $!{N;ba};};/192\.168\.17\.34/d;}' filename
would delete the entry containing the fixed address 192.168.17.34. Note that . has been escaped so as to match a literal ..
If the entries in the file are guaranteed to be separated by a blank line, you could simplify it by saying:
sed -e '/./{H;$!d;}' -e 'x;/pattern/d' filename
where the pattern in the command above denotes the entry to be deleted.
Add the -i option for in-place editing:
sed -i -e :a -e '/host my-system4/,/}/ { /}/!{ $!{N;ba};};/192\.168\.17\.34/d;}' filename
or
sed -i -e '/./{H;$!d;}' -e 'x;/pattern/d' filename
One solution using vim.
vim -u NONE -N \
-c 'set backup | g/\vfixed-address\s+192\.168\.17\.34/ normal dap' \
-c 'x' infile
It creates a backup of the file appending ~ to the name. Remove set backup and the pipe if you want to risk yourself to lose data. It uses a global replacement to search the line fixed-address 192.168.17.34 and selects the whole paragraph to delete it. The last x command saves the modified file.

Bash - find a keyword in a file and delete its line [duplicate]

This question already has answers here:
How to delete from a text file, all lines that contain a specific string?
(21 answers)
Closed 9 years ago.
I'd like to give a keyword, find the line where this keyword aṕpears in a file and erase the entire line.
This is what I got but it is not working as it should:
KEYWORD='domain.com'
cat /etc/hosts | grep -v "$KEYWORD" > /etc/hosts
UPDATE
This is what worked for me:
sed -i_bak -e '/domain\.com/d' /etc/hosts
However, as I had two lines with "domain.com", is there a way to tell sed to erase only the line where the exact keyword "domain.com" appears
This is the original content of /etc/hosts:
127.0.0.1 localhost localhost.localdomain
222.111.22.222 hvn.domain.com
222.111.22.222 domain.com
Here's how it end up after the command sed -i_bak -e '/domain\.com/d' /etc/hosts:
127.0.0.1 localhost localhost.localdomain
I tried sed -i_bak -e '/\<namourphoto\.com\.br\>/d' ~/Desktop/hosts but it didn't work.
CONCLUSION
This is the code I came up with (based on the help the fine folks have given me):
D=domain.com
DOMAIN=`echo "$D" | sed 's/\./\\\\./g'`
sed -i_bak -e "/[\t]$DOMAIN/d" /etc/hosts
Note that:
I am counting that there is always a tab before the domain to be erased
I am automatically escaping the dots of the domain name.
Use the stream editor, sed:
sed -i ".bak" '/culpa/d' test.txt
The above will delete lines containing culpa in test.txt. It will create a backup of the original (named test.txt.bak) and will modify the original file in-place.
Pipe it to another file, not the same one that you're reading from, and be careful with the useless use of cat.
grep -v "$KEYWORD" /etc/hosts > newfile
Apart from the fine answer given regarding sed, you can also use Perl and a little improved regex to solve this:
perl -pi.old -e 's/.*\sdomain\.com\s*\n//' file
Notice I'm considering domain.com will be isolated by space characters (tabs or spaces, and so on), and that nothing but zero or more spaces will appear after it until the newline. Similarly to -i in sed,
-i.old in perl sets the $^I variable, and your original file will receive the changes while a copy will be kept under the old name and a .old appended.
If you want to only delete last line of your example file
sed -i '/[[:space:]]\+domain\.com/d' test.txt

Resources