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.
Related
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
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.
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
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
I manage a computer lab with 40 ubuntu machines and I have cobbled together this command to find the total disk usage of files larger than 100M in the students' home directories:
for i in `cat ./lab-machines.txt ` ; do ssh $i "nohup find /home -size +100M -print0 | du --files0-from=- -ch | tail -1 && hostname && ls /home" ; done > lab-disk-usage.txt
The file "lab-machines.txt" contains the hostnames of the computers on a separate line each. The command runs from a server that has been configured with password-less logins into the lab machines for the root user. The output in the file lab-disk-usage.txt contains something like this for every machine (I've inserted comments in parenthesis):
69G total
hostname
student-username (changes)
admin-username (always the same)
lost+found (always the same)
I would like the output to look like this for each machine:
69G hostname student-username
I am not familiar enough with text filtering to get this done in time. Can you help?
try this:
awk -vORS=" " 'NR==1{sub("total","")}NR<=3' file
Pipe Output Through tr Command
You might try a simpler solution, such as piping your output through the tr command. For example:
tr -s "\n" ' ' < lab-disk-usage.txt
This assumes there's only one record in the file, though. If you plan on having multiple records, you'll want to filter each record through the tr pipeline first before appending it to the output file. For example:
your_pipeline_commands | tr -s "\n" ' ' > lab-disk-usage.txt
Use Perl's Flip-Flop Operator
If you have a set of multi-line records, you'll need to be more clever. Perl offers some advantages over AWK for handling multi-line records, including the flip-flop operator. For example:
perl -ne 'if ( /total/../^lost/ ) {
chomp $_; print $_ . " "
} else {
print "\n"
};
END { print "\n" };' lab-disk-usage.txt
Depending on your actual corpus, you may need to tweak the regular expression a bit to get things working right, but on my system it does the right thing.
Corpus for Testing Perl
69G total
hostname
student-username
admin-username
lost+found
69G total
hostname
student-username
admin-username
lost+found
Sample Output from Perl
69G total hostname student-username admin-username lost+found
69G total hostname student-username admin-username lost+found
I've slightly modified your example data:
69G total
host1
jane
admin-username
lost+found
65G total
host2
albert
admin-username
lost+found
This can get turned into a table:
[ghoti#pc ~/tmp]$ awk 'NR%5==1{size=$1} NR%5==2{host=$1} NR%5==3{user=$1; printf("%-8s%-16s%s\n", size, host, user)}' lab-disk-usage.txt
69G host1 jane
65G host2 albert
The essential thing her is that we're using a modulo operator (NR%5) to figure out where we are in each set of five lines.
If you can't rely on five lines per set, then please clarify how your input data is structured. There are other ways we can detect record boundaries, like looking for /[0-9]+G total$/, if NR%5 can't be used:
[ghoti#pc ~/tmp]$ awk '/G total$/{size=$1; getline host; getline user; printf("%-8s%-16s%s\n", size, host, user)}' lab-disk-usage.txt
69G host1 jane
65G host2 albert
This is basically just an awk version of potong's GNU sed suggestion, which could also be made portable (i.e. not just GNU sed) as:
[ghoti#pc ~/tmp]$ sed -ne '/G total/{s/ .*//;N;N;s/\n/ /g;p;}' lab-disk-usage.txt
69G host1 jane
65G host2 albert
This might work for you (GNU sed):
sed -nr '/ total/{N;N;s/( total\s*)?\n/ /gp}' file
If there are no empty lines between the records you could introduce one first:
awk '/total/{print x}1' | awk '{print $1,$3,$4}' RS= OFS='\t'
With file contents:
69G total
host1
jane
admin-username
lost+found
65G total
host2
albert
admin-username
lost+found
This produces:
69G host1 jane
65G host2 albert
If there already is an empty line between the records you could skip the part before the pipe and use:
awk '{print $1,$3,$4}' RS= OFS='\t' file