How to replace the second match without replacing the entire line - sed

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

Related

sed command for postgresql pg_hba.conf modification

I want to edit content of pg_hba.conf using sed :
# "local" is for Unix domain socket connections only
local all all peer
to
# "local" is for Unix domain socket connections only
local all all trust
For know, I use this command that works:
sed -i 's/local all all peer/local all all trust/' pg_hba.conf
But I'm looking for a way to bypass all theses spaces
You can use
sed -E -i 's/local([[:space:]]+)all([[:space:]]+)all([[:space:]]+)peer/local\1all\2all\3trust/' test.conf
Or, since it seems like you have a GNU sed:
sed -E -i 's/local(\s+)all(\s+)all(\s+)peer/local\1all\2all\3trust/' test.conf
And certainly you can do as potong did in the comments and reduce this to
sed -E -i 's/(local\s+all\s+all\s+)peer/\1trust/' test.conf
Note:
-E enables the POSIX ERE syntax (no need to escape + and (...))
([[:space:]]+) / (\s+) defines capturing groups with IDs starting with 1 that match one or more whitespaces
\1, \2 and \3 are placeholders, backreferences to the appropriate group values.
In s/(local\s+all\s+all\s+)peer/\1trust/, you capture the whole part before peer and match peer, then the whole match is replaced with the part before peer (with \1) + trust.
You could use this sed if the string that needs to be changed is not always going to be peer
$ sed -i 's/\S*$/trust/' input_file
# local is for Unix domain socket connections trust
local all all trust

Edit a configuration file using bash script

I have a bash script that needs to modify .ssh/config. My goal is to change the HostName value of server1 using sed and I have already managed to do it, but in the file there are more HostName and sed modifies them all. I have tried specifying to sed to stop at the first occurrence but continues to modify them all.
This is the file where I need to change the HostName of server1
Host server1
HostName 172.160.189.196
User admin
Port 353
Host server2
HostName 254.216.34.18
User user
Port 22
This is the command I give:
sed -i '0,/RE/s/HostName .*/HostName 14.208.54.132/' .ssh/config
Try using a sed range:
sed -i '/Host server1/,/HostName/ s/HostName .*/HostName 14.208.54.132/' .ssh/config
This will replace HostName in the range of lines between Host server1 and the first occurrence of HostName, which I think is what you want.
While awk is generally known to work on lines, it actually works with records, by default lines. A record can be defined by a record separator RS. If this variable is empty, it assumes that a record is given by text-blocks separated by one or more empty lines. With this you can do the following:
awk 'BEGIN{RS="";FS=OFS="\n";ORS="\n\n"}
($1~/server1/) {sub(/Hostname[^\n]*\n/,"Hostname 14.208.54.132" OFS)}
1' file
This is not short, but conceptually clean.
Obviously, you have to update the regex to match the hostname such that it is unique. If you also have a hostname server1a, then you will have to make sure that ($1~/server1/) does not match that.
You can use awk like this:
awk '$2=="server1" {f=1} f && /HostName/ {$0=" HostName 14.208.54.132";f=0} 1' file
Host server1
HostName 14.208.54.132
User admin
Port 353
Host server2
HostName 254.216.34.18
User user
Port 22
This might work for you (GNU sed):
sed '/\<server1\>/{:a;n;/HostName/!ba;s/\S\+/14.208.54.132/2}' file
Focus on a line containing server1 then read additional lines until one containing HostName and substitute the second field for the desired result.

Command to replace string in one file with content of another file

The file /var/cpanel/mainip contains the main IP of my server and nothing else.
The file /etc/csf/csf.blocklists contains a list of firewall blocklists, and part of the file contains a line with an example IP address 1.2.3.4
Normally when installing CSF firewall software on a new server, I will manually replace the example IP address with the server's main IP address. This is required to successfully fetch firewall blocklists from some providers.
To simplify the setup process, I want a command that will replace the example IP address 1.2.3.4 with my main server IP so I don't have to manually do it.
I've tried a sed command, I guess I'm close to the correct command but not quite there yet. Please can you help?
sed -i '/1.2.3.4/ { r /etc/csf/csf.blocklists }' /var/cpanel/mainip
This code is not right because it outputs an error:
sed: -e expression #1, char 0: unmatched `{'
When successful, it should replace 1.2.3.4 with the actual server IP address.
sed -i "s/1\.2\.3\.4/$(</var/cpanel/mainip)/" /etc/csf/csf.blocklists
should do the job.
You only need to specify
sed -i '/1.2.3.4/r /etc/csf/csf.blocklists' /var/cpanel/mainip
In fact even the space is optional ; following will work too
sed -i '/1.2.3.4/r/etc/csf/csf.blocklists' /var/cpanel/mainip
You are simply specifying the pattern /1.2.3.4/ and a command after that
The command could be r for read, w for write, d for delete and a few others
An example of d to delete the entry would be
sed -i '/1.2.3.4/d' /var/cpanel/mainip

How to interchange the fqdn <-> hostname in hosts file

I am trying to update the format in /etc/hosts file.
sample
# more /etc/hosts
14.5.10.13 host1 host1.mydomain.com
14.5.10.14 host2 host2.mydomain.com
#
to
14.5.10.13 host1.mydomain.com host1
14.5.10.14 host2.mydomain.com host2
I tried this but didnt worked. Please suggest.
#sed 's/host{1,2} /host{1,2}.mydomain.com/' /etc/hosts
To swap the second and third fields in the file hosts but only if the second is host1 or host2:
$ awk '$2~/^host[12]$/{a=$2; $2=$3; $3=a} 1' hosts
# more /etc/hosts
14.5.10.13 host1.mydomain.com host1
14.5.10.14 host2.mydomain.com host2
#
Here, $2~/^host[12]$/ selects only those lines whose second field matches one of the hosts of interest. For those lines, the second and third field are swapped. The final 1 is awk's cryptic shorthand for print-the-line.
To do something similar with sed:
$ sed -E '/ host[12] /{s/ (host[12]) ([[:alnum:].]*)/ \2 \1/}' hosts
# more /etc/hosts
14.5.10.13 host1.mydomain.com host1
14.5.10.14 host2.mydomain.com host2
#
Here, / host[12] / selects only those lines that contain host1 or host2 surrounded by spaces. For those lines, host1 or host2 is swapped with the word that follows.

Nmap scan range output file problem

Okay, I want to have Nmap scan an IP range for computers with a certain port open (port 80 in this case) and have it output all the IP's it finds into a text file, stored in this format:
192.168.0.1
192.168.0.185
192.168.0.192
192.168.0.195
So to output the file, I tried using this command:
nmap -sT -p 80 -ttl 40 192.168.0.0-255 -oG - | grep "80/open" > output.txt
Where "output.txt" is the output file that contains the results. So a line of output.txt looks
like this:
Host: 192.168.0.1 () Ports: 80/open/tcp//http///
So I basically want it only to output the IP address with port 80 open, and nothing else.
I want it to not output the "Host: " or the "()" and "Ports: 80/open/tcp//http///" lines. So is there anyway I can have Nmap not put that stuff into the output file? Or make it only
output the IP addresses? I tried looking at the map page, it was of little help. And I looked all over the Internet and that wasn't very useful either. So does anyone know how I can do this? Thanks
Awk is your friend!
$ nmap -sT -p 80 192.168.0.0/24 -oG - | awk '/ 80\/open/{print $2}' > output.txt
This will find lines with port 80 open (notice the space before 80, if you plan to scan more than the one port!), and print field 2, splitting on whitespace. Another way to do it would be:
$ nmap -sT -p 80 --open 192.168.0.0/24 -oG - | awk '$4=="Ports:"{print $2}' > output.txt
This one uses the --open argument to Nmap to only produce output for hosts with open ports. The awk command checks that this is a "Ports" line, not a "Status" line (which may only show up when using -v, but I'm not positive) before printing the IP address.
Note that it is usually in your best interests to save the scan results to a file, to avoid needing to repeat the scan if you decide to extract some different information. If you choose to do this, I would recommend using the XML output (-oX), since there are lots of analysis tools that have parsers built for it already.
Having nmap produce exactly what you want would indeed be nice. But as a more general solution:
$ nmap ... | grep ... | tr '/' ' ' | awk '{ print $2,$5; }
192.168.0.1 80
Or maybe:
nmap ... | grep ... | tr '/' ' ' | cut -d' ' -f2,8
I found a script called scanreport.sh very useful. Although its not necessary, you could just use awk as suggested, but thought it might be of interest.
It gives the ability to output the nmap results nicely by service or port (with highlighting). It uses the grep-able output from nmap (-oG) after a quick tidy from grep -v ^# nmapoutput.txt > report.txt
Example
nmap -sS 192.168.1.22 -oG /directory/of/choice/results.txt
grep -v ^# results.txt > report.txt
./scanreport.sh -f report.txt
Host: 192.168.1.22 ()
22 open tcp ssh OpenSSH 5.3p1 Debian 3ubuntu4 (protocol 2.0)
80 open tcp http Apache httpd 2.2.14 ((Ubuntu))
./scanreport.sh -f report.txt -p 80
Host: 192.168.1.22 ()
80 open tcp http Apache httpd 2.2.14 ((Ubuntu))
./scanreport.sh -f report.txt -s ssh
Host: 192.168.1.22 ()
22 open tcp ssh OpenSSH 5.3p1 Debian 3ubuntu4 (protocol 2.0)
Plenty of stuff on google about it but here a link to one ref.
./scanreport.sh