Adding scan lines with sed file - sed

I'm trying sed to find test and insert line
example
iface eth0:5 inet static
address 1.1.1.1
netmask 255.255.255.0
iface eth0:6 inet static
address 2.2.2.2
netmask 255.255.255.0
replace
auto eth0:5
iface eth0:5 inet static
address 1.1.1.1
netmask 255.255.255.0
auto eth0:6
iface eth0:6 inet static
address 2.2.2.2
netmask 255.255.255.0

sed should work, but this simple awk can do it too:
awk '/iface/ {print "auto",$2}1' file
auto eth0:5
iface eth0:5 inet static
address 1.1.1.1
netmask 255.255.255.0
auto eth0:6
iface eth0:6 inet static
address 2.2.2.2
netmask 255.255.255.0

Here you go:
sed -e 's/iface \([^ ]*\) .*/auto \1\'$'\n''&/' file
The \([^ ]*\) will capture the name of the interface so we can use by \1 in the replacement, so we can insert as auto \1, followed by a newline, followed by & which means the original line.
If you want to replace inline, you can use the -i flag, for example:
sed -ie 's/iface \([^ ]*\) .*/auto \1\'$'\n''&/' /etc/network/interfaces

If you are OK with perl:
perl -i -lane 'if(/^iface/){print "auto $F[1]\n$_"}else{print}' your_file
Remember this makes an inplace replacement.
If you donot wish for in place replacement then,
perl -lane 'if(/^iface/){print "auto $F[1]\n$_"}else{print}' your_file
You can use awk as well:
awk '/^iface/{print "auto "$2}1' your_file

Related

sed delete multi lines after a match then do replace

I have a file /etc/config/network
config interface loopback
option ifname lo
option proto static
option ipaddr 127.0.0.1
option netmask 255.0.0.0
config interface lan
option ifname eth0
option type none
option proto static
option ipaddr 192.168.1.1
option netmask 255.255.255.0
config interface debug
option ifname usb0
option type none
option proto static
option ipaddr 172.18.0.18
option netmask 255.255.255.0
I want to rewrite it to be
config interface loopback
option ifname lo
option proto static
option ipaddr 127.0.0.1
option netmask 255.0.0.0
config interface lan
option ifname eth0
option type none
option proto dhcp
config interface debug
option ifname usb0
option type none
option proto static
option ipaddr 172.18.0.18
option netmask 255.255.255.0
I proceed this in this way:
find static then delete two lines after(exclude line static)
replace static with dhcp
Tried using sed '/static/{n;d}' /etc/config/network | sed -e 's/static/dhcp/' -e '/dhcp/{n;d}' > /etc/config/network which is not so neat.
Could this be like sed -i -e <delete pattern> -e <replace pattern> /etc/config/network ?
I think awk would be a good choice for this type of text processing. It gives you great opportunity for customization.
/^config interface/ { interface=$3; option="" }
$1=="option" { option=$2 }
interface == "lan" && option == "proto" { $3 = "dhcp" }
interface == "lan" && ( option == "ipaddr" || option == "netmask") { option=""; next }
And if the formatting bothers you, you might replace the line that sets $3 with:
interface == "lan" && option == "proto" { gsub(/static/,"dhcp",$0) }
Of course, if you still do want to just one-off this in sed, and the input file is always going to be formatted that way, something like this might be sufficient:
sed '/config interface lan/,/^$/{s/static/dhcp/;/ipaddr/d;/netmask/d;}'
This works by considering the space between "config interface lan" and the next blank line, to restrict the commands to those inside the curly braces. It can adjust to some slight variations in options order, but it needs that blank line.
Using GNU sed
$ sed -i.bak '/config interface lan/,/^$/{/static/{N;N;s/static.*/dhcp/}}' /etc/config/network
config interface loopback
option ifname lo
option proto static
option ipaddr 127.0.0.1
option netmask 255.0.0.0
config interface lan
option ifname eth0
option type none
option proto dhcp
config interface debug
option ifname usb0
option type none
option proto static
option ipaddr 172.18.0.18
option netmask 255.255.255.0

translate pcregrep into Perl one-liner

I need to find all active network interfaces on new macOS. That means the following one-liner with pcregrep will not work:
ifconfig | pcregrep -M -o '^[^\t:]+(?=:([^\n]|\n\t)*status: active)'
because pcregrep is no default install on macOS.
I tried to translate it into egrep to no avail, because a positive lookahead is not possible, right?
So I tried with a one-liner in perl. But the following command is not working, because the switch -pe is not gobbling up all lines together. I tried with -p0e too.
ifconfig | perl -pe 'while (<>) {if (/^[^\t:]+(?=:([^\n]|\n\t)*status: active)/){print "$1";};}'
If I search with a positive lookahead the same line, it is working; for example:
ifconfig | perl -pe 'while (<>) {if (/^([^\t:]+)(?=:([^\n]|\n\t)*mtu 1380)/){print "$1";};}'
utun0
A typical output of ifconfig:
en10: flags=8863<UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST> mtu 1500
options=6467<RXCSUM,TXCSUM,VLAN_MTU,TSO4,TSO6,CHANNEL_IO,PARTIAL_CSUM,ZEROINVERT_CSUM>
ether 00:e0:4c:68:01:20
inet6 fe80::1470:31b9:a01c:6f5e%en10 prefixlen 64 secured scopeid 0xd
inet 192.168.178.39 netmask 0xffffff00 broadcast 192.168.178.255
inet6 2003:ee:4f1a:ce00:864:f90c:9a11:6ad9 prefixlen 64 autoconf secured
inet6 2003:ee:4f1a:ce00:d89a:7e34:6dd4:1370 prefixlen 64 autoconf temporary
nd6 options=201<PERFORMNUD,DAD>
media: autoselect (1000baseT <full-duplex>)
status: active
The expected result would be:
en10
I am on macOS Monterey, zsh and perl 5.34
Thank you for your help
marek
Since output of ifconfig normally† has a multiline block of text for each interface, all separated by blank lines, it is convenient to read it in paragraphs (-00). Then the rest simplifies a lot
ifconfig -a | perl -00 -nE'say $1 if /^(.+?)\s*:.*?status:\s+active/s'
We still need the /s modifier, making . match a newline as well, as each paragraph itself is a multiline string and the pattern needs to match across multiple lines.
† Except that it doesn't on the MacOS used for this question -- there are no blank lines separating blocks for interfaces. Then there is no point in seeking paragraphs (breaking on newline which isn't) and this answer doesn't work for that system.
Here is then a classic line-by-line approach that does -- set the interface name at the first line for that interface's output (no spaces at line beginning), then test for active status
perl -wnE'$ifn=$1, next if /^(\S[^:]+?)\s*:/; say $ifn if /status:\s+active/' file
This allows spaces inside an interface name, what is very unlikely (and perhaps not even allowed). For a more restrictive pattern, which doesn't allow spaces in the name, use /^(\S+?)\s*:/ (or the more efficient /^([^:\s]+)/). The \s* and the preceding ? are there only to make it not capture the trailing spaces (right before :), if any were possible.
This works in the case when there are empty lines between interface blocks as well.
You can use
perl -0777 -nE 'say "$&" while /^[^\n\r\t:]+(?=:(?:.*\R\t)*status:\h+active)/gm'
See the regex test.
Here, -0777 slurps the file so that a regex could match multiline text spans (the M equivalent that exposes the newlines to the pattern in pcregrep), say "$&" prints all matched substrings (the o equivalent, also see g flag).
I edited the [^\t:]+ to match any one or more chars other than tabs, colons and also CR/LF chars. Also, I replaced ([^\n]|\n\t)* into a more efficient (?:.*\R\t)* that matches zero or more occurrences of any zero or more chars other than line break chars till the end of a line (.*), then a line break sequence (\R), and then a tab char (\t).
Also, note the m flag to make ^ anchor also match any line start position.
perl's -n and -p command-line switches add an implicit while (<>) {...} block around the -e code, and in addition -p prints the line at the end of each iteration. So you need to change the -p to -n and only print out the lines which match; and remove the extra and unneeded while loop. So something like
ifconfig | perl -ne 'print if /...../'

How to replace the second match without replacing the entire line

I have a host file with the below lines:
127.0.0.1
127.0.1.1 servername
The server IP is : 192.168.1.1 which is represented by the IP variable below:
IP=192.168.1.1
I want to replace the second entry that starts with 127.0 with $IP i.e:
instead of :
127.0.1.1 servername
it should be :
192.168.1.1 servername
I tried to use the below sed statement :
sed "0,/127.0.*/! s/127.0.*/$IP/" /etc/hosts
But its replacing the entire line removing the servername as well ,and leave me with :
127.0.0.1 localhost.localdomain localhost
192.168.1.1
its should be :
192.168.1.1 servername
. is a regex-active character, you need to escape it with a backslash (\),
.* will match everything until EOL, use a negated character class for matching everything until first blank character instead.
sed '0,/^127\.0\./! s/^127\.0\.[^[:blank:]]*/'"$IP"'/' file
Btw, you could get this task done way more safely&robustly using awk:
$ awk -v IP="$IP" 'n<2 && index($1,"127.0.")==1 && ++n==2 { $1=IP } 1' file
127.0.0.1
192.168.1.1 servername

Perl script for getting inet from ip command

I am trying to get the inet using ip command it is working fine on cmd prompt but if i add it in perl script, it is not executing as expected. Script is below:-
ip.pl
use strict;
my $a = `ip -f inet addr show eth0| grep -Po 'inet \K[\d.]+'`;
chomp($a);
print $a;
Executing above with "perl a.pl" returns nothing but "ip -f inet addr show eth0| grep -Po 'inet \K[\d.]+'" returns inet value.
How to get it executed using perl script?
Turn on warnings to get a hint:
Unrecognized escape \K passed through at ./1.pl line 5.
Unrecognized escape \d passed through at ./1.pl line 5.
Single quotes inside backticks aren't nested, you need to backslash the backslashes:
my $a = `ip -f inet addr show eth0| grep -Po 'inet \\K[\\d.]+'`;
Using $a for a lexical variable is wrong, it can cause weird bugs when sort is later used which uses $a as a special variable. Use a more meaningful name.
Moreover, calling grep from Perl is usually not needed, you can match the string in Perl itself:
my ($ip) = `ip -f inet addr show eth0` =~ /inet ([\d.]+)/;
or
my ($ip) = `ip -f inet addr show eth0` =~ /inet \K[\d.]+/g;

How do I use sed properly to insert text into a file using a variable?

How do I use sed properly to insert text into a file using a variable?
MYNIC_IP=`ip addr show eth0 | grep eth0 | awk '{ print $2; }' | sed 's/\/.*$//'`
Ok cool.
echo $MYNIC_IP
eth0: 172.31.43.190
sudo sed -i -e '1i'$MYNIC_IP /usr/share/nginx/html/index.html
This results in part of the variable being added... just eth0:
You need to quote it so sed sees a single string including the space:
sed -i -e "1i\\
$MYNIC_IP"
With double quotes, you can still use variables inside and they will be expanded.