Issue
I need to setup static IP for a device via script. To do so, I need to process its /etc/dhcpcd.conf
file, which looks like this:
... blah blah
# fallback to static profile on eth0
#interface eth0
#fallback static_eth0
interface eth0
static ip_address=192.168.90.120
static routers=192.168.90.1
interface eth1
... blah blah
I need to remove all currently configured parameter of eth0
and replace it with my own. So I will need to:
- find the line that matches
^interface eth0
- remove matching line and all none-empty lines immediately below it, until an empty line or the end of the file is encountered
- write my own config at the bottom of the file
How can I do this with standard linux command line tools such as grep
, sed
, as well as bash scripting. I hope to avoid python
or perl
or any other programming languages.
EDIT
As suggest in the comment:
- My own config looks like:
interface eth0
static ip_address=172.16.1.100/24
static routers=172.16.1.1
static domain_name_servers=172.16.1.10
The key problem here is that the lines of configuration "block" is not fixed, it might be 3, 4 or 5 lines. The only way I can think of is to use blank lines to set its "boundary".
- My current solution uses a Go program to do the processing, of course that way I can do whatever I want. But I would like to solve this problem using shell scripting and native linux tools only. That's why I said I would like to avoid
python
,perl
etc.
This is NOT a homework question. I ask simply want to learn how to do multi-line matching using native tools -- if there is such an "elegant" way. If there is not, fine, I can using programming languages to solve it.
EDIT
I did a few tests with the accepted answer, the last variant: sed with external config looks just what I want, so I do:
- test.sh:
#!/bin/bash
sed '/^interface eth0$/,/^$/d;$rmc.conf' dhcpcd.conf
- mc.conf:
interface eth0
static ip_address=172.16.1.100/24
static routers=172.16.1.1
static domain_name_servers=172.16.1.10
- dhcpcd.conf
# blah blah
interface wlan0
static ip_address=10.77.1.1/24
nohook wpa_supplicant
interface eth0
static ip_address=192.168.90.120
static routers=192.168.90.1
All three files in the current directory, the output of test.sh
is:
# blah blah
interface wlan0
static ip_address=10.77.1.1/24
nohook wpa_supplicant
i.e. the content of mc.conf
is NOT appended to output. I can of course do this:
#!/bin/bash
cat dhcpcd.conf|sed '/^interface eth0$/,/^$/d'
cat mc.conf
But it is nice to know how to actually make the r
command of sed
work.
BTW, I am running Ubuntu 23.04.
Solution
With awk
:
$ mc="interface eth0
static ip_address=172.16.1.100/24
static routers=172.16.1.1
static domain_name_servers=172.16.1.10"
$ awk -v mc="$mc" '/^interface eth0$/,/^$/ {if(!/^$/) next} {print} END {print mc}' /etc/dhcpcd.conf
... blah blah
# fallback to static profile on eth0
#interface eth0
#fallback static_eth0
interface eth1
... blah blah
interface eth0
static ip_address=172.16.1.100/24
static routers=172.16.1.1
static domain_name_servers=172.16.1.10
Explanations: we skip (next
) all lines from the interface eth0
line to the next empty line excluded, we print all other lines and at the end (END
block) we print the content of awk
variable mc
. The -v mc="$mc"
option declares awk
variable mc
and assigns it the value of the previously declared bash variable with same name.
Or, if you can store your own config in another file, say mc.conf
:
awk '/^interface eth0$/,/^$/ {if(NR==FNR && !/^$/) next} {print}' /etc/dhcpcd.conf mc.conf
Explanation: NR==FNR
is true only for the first file (/etc/dhcpcd.conf
).
With sed
:
$ sed '/^interface eth0$/,/^$/{
/^$/!{
$!d
s/.*//
}
}
$a\
interface eth0\
static ip_address=172.16.1.100/24\
static routers=172.16.1.1\
static domain_name_servers=172.16.1.10' /etc/dhcpcd.conf
... blah blah
# fallback to static profile on eth0
#interface eth0
#fallback static_eth0
interface eth1
... blah blah
interface eth0
static ip_address=172.16.1.100/24
static routers=172.16.1.1
static domain_name_servers=172.16.1.10
Explanations: delete (d
) all lines from the interface eth0
line to the next empty line excluded, print the others (the default with sed
) and, at the end ($
), append (a
) the new configuration. There is a special case when the last line to delete is also the last of /etc/dhcpcd.conf
: in this case it is not deleted because it would end the processing before the new config is inserted. Instead it is replaced with an empty line.
Or, if you can store your own config in another file, say mc.conf
:
sed '/^interface eth0$/,/^$/{
/^$/!{
$!d
s/.*//
}
}
$rmc.conf' /etc/dhcpcd.conf
(sed
command r
copies the contents of specified file to standard output).
If your sed
is GNU sed
and if you don't have NUL
bytes in /etc/dhcpcd.conf
we can simplify:
sed -Ez 's/(^|\n)interface eth0\n(.+\n)*/\1/m;$rmc.conf' /etc/dhcpcd.conf
-E
for extended regular expressions, -z
to slurp the whole file as one single line. With the m
modifier of the substitute command periods don't match newlines.
Finally, if your entries are always separated by one or more blank lines, and you want to update several of them at once, GNU awk
is probably a reasonable choice:
awk -v RS='' -v ORS='\n\n' -F'\n' '
FNR==NR {a[$1]=$0;next} {print a[$1]?a[$1]:$0}' mc.conf /etc/dhcpcd.conf
We set the input record separator (RS
) to the empty string, such that records are separated by empty lines. We set the output record separator (ORS
) to 2 newline characters such that the same format is used for the output. We also set the input field separator to newline. The rest is quite simple: while parsing the first file (mc.conf
) containing your new configurations we add each record to associative array a
, indexed by the first line of the record. When parsing the second file, if a record has its first line that matches a key of array a
it is replaced by the corresponding content of a
. Else it is unmodified.
Answered By - Renaud Pacalet Answer Checked By - Cary Denson (WPSolving Admin)