extract multiple strings with regex - awk

I have the following text:
Disable Automounting /usr/bin/systemctl is-enabled autofs | /usr/bin/awk '{print} END {if(NR==0) print "disabled" }'
Ensure nodev option set on /home partition for i in /etc/fstab; do /bin/echo '==========' `/bin/ls -la $i`; /bin/cat $i; done
I would like to get the cmd line in both line, by matching :
what is between / and }' -> usr/bin/systemctl is-enabled autofs | /usr/bin/awk '{print} END {if(NR==0) print "disabled"
OR what is between for and done -> i in /etc/fstab; do /bin/echo '==========' /bin/ls -la $i; /bin/cat $i;
I managed to do it with one matching group : for(.*)done
I can't with both though.
Thanks

You can easily achieve what you want using the lookahead and lookbehind features of PCRE and since GNU grep supports PCRE (while sed and awk do not), you can use the following:
#!/bin/sh
grep -Po "(?<=\/)(.*?)(?= }')|(?<=for )(.*?)(?= done)" <<-'EOF'
Disable Automounting /usr/bin/systemctl is-enabled autofs | /usr/bin/awk '{print} END {if(NR==0) print "disabled" }'
Ensure nodev option set on /home partition for i in /etc/fstab; do /bin/echo '==========' `/bin/ls -la $i`; /bin/cat $i; done
EOF
(?<=\/)(.*?)(?= }') matches what's between / and }' while (?<=for )(.*?)(?= done) matches what's between for and done. | between the two expressions is just the PCRE OR operator.

Related

Running "ip | grep | awk" within a sed replacement

Problem Set (Raspberry Pi OS):
I have a file example.conf that contains a line IPv4addr=XXXXX. I am attempting to change this to the IP that is generated the in the command
ipTest=$(ip --brief a show | grep eth0 | awk '{ print $3 }')
I want to automate this file change during a script install.sh, the line I am attempting is:
IPtest=$(ip --brief a show | grep eth0 | awk '{ print $3 }')
sudo sed -e "/IPv4addr/s/[^=]*$/$IPtest/" example.conf
Returns error:
sed: -e expression #1, char 32: unknown option to `s'
A simple line in that code works, such as SimpleTest='Works'
Any thoughts? I am open to other solutions as well, however I am not an experienced linux user so I am using the tools I know to work with other problem sets.
$IPtest contains the / character; try something like this:
IPtest=$(ip --brief a show | grep eth0 | awk '{ print $3 }')
sudo sed -e '/IPv4addr/s#[^=]*$#'"$IPtest"'#' example.conf
You can shorten your variable and allow awk to do the job of grep at the same time
IPtest=$(ip --brief a s | awk '/eth0/{print $3}')
Using sed grouping and back referencing
sed -i.bak "s|\([^=]*.\).*|\1$IPtest|" example.conf

AWKing or GREPing brackets [ ]?

I've searched all over and couldn't find a solution.
How would I awk or grep the following:
$ mbimcli -d /dev/cdc-wdm0 -p --query-ip-configuration
[/dev/cdc-wdm0] IPv4 configuration available: 'address, gateway, dns'
IP [0]: '11.22.333.44/55'
Gateway: '14.13.198.4'
DNS [0]: '172.17.1.101'
DNS [1]: '172.17.1.102'
DNS [2]: '172.17.1.101'
DNS [3]: '172.17.1.102'
So that I end up with:
11.22.33.44/55
I've tried a bunch of different combinations with both grep and awk and couldn't find a solution.
Using cat file as I don't have mbimcli -d /dev/cdc-wdm0 -p --query-ip-configuration:
$ cat file | awk -F"'" '/IP \[/{print $2}'
11.22.333.44/55
$ cat file | awk -F"'" '/Gateway/{print $2}'
14.13.198.4
or maybe this is all you need if the output of that command always looks like the example you posted:
$ cat file | awk -v RS= -F"'" '{print $5}'
11.22.333.44/55
$ cat file | awk -v RS= -F"'" '{print $8}'
14.13.198.4
You can do this in a single awk:
mbimcli -d /dev/cdc-wdm0 -p --query-ip-configuration |
awk '$1 == "IP" {gsub(/\047/, "", $NF); print $NF}'
11.22.333.44/55
something like this:
grep '[0-9]' file_with_text | awk '{print $NF}
grep [0-9] only lines with numbers and pipe the output into awk.
The $NF will return the last element.
If you want only the line that has the /, just add it to grep [0-9]/.
Also, for a complete answer, you can pipe the output of the command into grep:
mbimcli -d /dev/cdc-wdm0 -p --query-ip-configuration | grep '[0-9]/' | awk '{print $NF}
I would harness tr for preprocessing and GNU AWK for processing, let file.txt content be
IP [0]: '11.22.333.44/55'
Gateway: '14.13.198.4'
DNS [0]: '172.17.1.101'
DNS [1]: '172.17.1.102'
DNS [2]: '172.17.1.101'
DNS [3]: '172.17.1.102'
then
cat file.txt | tr -d "'" | awk '/IP/{print $NF}'
output
11.22.333.44/55
Explanation: use tr to delete ' then awk to print last column ($NF) if row contain IP.
(tested in tr (GNU coreutils) 8.30 and GNU Awk 5.0.1)

Assign variable to cut -f field

Using cut, I want to know how to use it as:
awk -v id=3 -v RS= -F '::' '($1==id) {print $3}' jenny | a=1 ;cut -d$'\n' -f$a
I want to use it in a loop where i is replaced with, e.g., -f 1...3
Input
0::chkconfig --list autofs::
autofs 0:off 1:off 2:on 3:on 4:on 5:on 6:off
1::grep "^PROMPT=" /etc/sysconfig/init::
PROMPT=yes
2::rpm -q prelink::
prelink-0.4.0-2.el5
3::if [ -z "$(grep -l "hard core" /etc/security/limits.conf /etc/security/limits.d/*)" ]; then echo "empty"; else echo -e "$(grep -l "hard core" /etc/security/limits.conf /etc/security/limits.d/*)"; fi::
/etc/security/limits.conf
/etc/security/limits.d/test
4::sysctl fs.suid_dumpable::
fs.suid_dumpable = 0
5::stat /etc/motd::
File: `/etc/motd'
Size: 17 Blocks: 16 IO Block: 4096 regular file
Device: fd00h/64768d Inode: 10125343 Links: 1
Access: (0644/-rw-r--r--) Uid: ( 0/ root) Gid: ( 0/ root)
Access: 2019-04-09 07:56:19.000000000 +0500
Modify: 2019-03-30 19:22:13.000000000 +0500
Change: 2019-03-30 19:22:13.000000000 +0500
Expected Output
/etc/security/limits.conf
/etc/security/limits.d/test
As field 1 and field currently it's all coming in $3. I tried separating with newline in awk; it doesn't seem to catch.
To get your desired output from the given input, try:
$ awk '/^$/{f=0} f{print} /3::/{f=1}' file
/etc/security/limits.conf
/etc/security/limits.d/test
To get only one output line as selected with a variable i:
$ awk -v i=1 '/3::/{n=NR+i} n==NR' file
/etc/security/limits.conf
$ awk -v i=2 '/3::/{n=NR+i} n==NR' file
/etc/security/limits.d/test
The awk variable i can, of course, be set to the value of a shell variable i:
$ i=2
$ awk -v i="$i" '/3::/{n=NR+i} n==NR' file
/etc/security/limits.d/test
The stanza can also be selected from a variable:
$ i=2
$ k=3
$ awk -v i="$i" -v k="$k" -F:: '$1==k{n=NR+i} n==NR' file
/etc/security/limits.d/test
How it works:
-v i="$i" -v k="$k"
These options set awk variable i and k to the values of the shell variables $i and $k, respectively.
-F::
This sets the field separator to ::.
$1==k {n=NR+i}
If the first field of the current line equals the variable k, then set variable n to the current line number, NR, plus i.
n==NR
If the current line number, NR, is n, then print this line.
With sed:
$ id=3; sed -En "/^$id::/,/^$/{/^[[:blank:]]*\//p}" jenny
/etc/security/limits.conf
/etc/security/limits.d/test
Explanations:
Your shell will interpret the command and replace id by its value.
/^$id::/,/^$/{} the scope {} will be executed only between the lines that starts with the value of id followed by :: (/^$id::/) until an empty line (/^$/)
/^[[:blank:]]*\//p for the lines that start with some POSIX blank character class (e.g. space/tab) followed by / print the line. This will print your two paths.
To specify a line:
$ id=3; line=1; sed -En "/^$id::/,/^$/{/^[[:blank:]]*\//p}" jenny | cut -d$'\n' -f"$line"
/etc/security/limits.conf
$ id=3; line=2; sed -En "/^$id::/,/^$/{/^[[:blank:]]*\//p}" jenny | cut -d$'\n' -f"$line"
/etc/security/limits.d/test
$ id=3; line=1; sed -En "/^$id::/,/^$/{/^[[:blank:]]*\//p}" jenny | sed -n "${line}p"
/etc/security/limits.conf
$ id=3; line=2; sed -En "/^$id::/,/^$/{/^[[:blank:]]*\//p}" jenny | sed -n "${line}p"
/etc/security/limits.d/test
Assuming you want to build onto your previous question rather than coming up with a completely different approach
$ awk -v id=3 -v lineNr=1 -v RS= -F '::' '$1==id{ split($3,lines,/\n/); print lines[lineNr+1] }' file
/etc/security/limits.conf
$ awk -v id=3 -v lineNr=2 -v RS= -F '::' '$1==id{ split($3,lines,/\n/); print lines[lineNr+1] }' file
/etc/security/limits.d/test

A script to change file names

I am new to awk and shell based programming. I have a bunch of files name file_0001.dat, file_0002.dat......file_1000.dat. I want to change the file names such as the number after file_ will be a multiple of 4 in comparison to previous file name. SO i want to change
file_0001.dat to file_0004.dat
file_0002.dat to file_0008.dat
and so on.
Can anyone suggest a simple script to do it. I have tried the following but without any success.
#!/bin/bash
a=$(echo $1 sed -e 's:file_::g' -e 's:.dat::g')
b=$(echo "${a}*4" | bc)
shuf file_${a}.dat > file_${b}.dat
This script will do that trick for you:
#!/bin/bash
for i in `ls -r *.dat`; do
a=`echo $i | sed 's/file_//g' | sed 's/\.dat//g'`
almost_b=`bc -l <<< "$a*4"`
b=`printf "%04d" $almost_b`
rename "s/$a/$b/g" $i
done
Files before:
file_0001.dat file_0002.dat
Files after first execution:
file_0004.dat file_0008.dat
Files after second execution:
file_0016.dat file_0032.dat
Here's a pure bash way of doing it (without bc, rename or sed).
#!/bin/bash
for i in $(ls -r *.dat); do
prefix="${i%%_*}_"
oldnum="${i//[^0-9]/}"
newnum="$(printf "%04d" $(( 10#$oldnum * 4 )))"
mv "$i" "${prefix}${newnum}.dat"
done
To test it you can do
mkdir tmp && cd $_
touch file_{0001..1000}.dat
(paste code into convert.sh)
chmod +x convert.sh
./convert.sh
Using bash/sed/find:
files=$(find -name 'file_*.dat' | sort -r)
for file in $files; do
n=$(sed 's/[^_]*_0*\([^.]*\).*/\1/' <<< "$file")
let n*=4
nfile=$(printf "file_%04d.dat" "$n")
mv "$file" "$nfile"
done
ls -r1 | awk -F '[_.]' '{printf "%s %s_%04d.%s\n", $0, $1, 4*$2, $3}' | xargs -n2 mv
ls -r1 list file in reverse order to avoid conflict
the second part will generate new filename. For example: file_0002.dat will become file_0002.dat file_0008.dat
xargs -n2 will pass two arguments every time to mv
This might work for you:
paste <(seq -f'mv file_%04g.dat' 1000) <(seq -f'file_%04g.dat' 4 4 4000) |
sort -r |
sh
This can help:
#!/bin/bash
for i in `cat /path/to/requestedfiles |grep -o '[0-9]*'`; do
count=`bc -l <<< "$i*4"`
echo $count
done

Linux Grep or Awk to find strings and store into array

I would like to print the string in the following pattern. And I would like to store it in a array. Please help me, I need O/p as follows
test11
orcl
My commands/Tries
egrep -i ":Y|:N" /etc/oratab | cut -d":" -f1 | grep -v "\#" | grep -v "\*" | tr -d '\n' | sed 's/ /\n/g' | awk '{print $1}'
Above commands O/p:
test11orcl
Contents of Oratab will be as follows,
[oracle#rhel6112 scripts]$ cat/etc/oratab
#
# This file is used by ORACLE utilities. It is created by root.sh
# and updated by the Database Configuration Assistant when creating
# a database.
# A colon, ':', is used as the field terminator. A new line terminates
# the entry. Lines beginning with a pound sign, '#', are comments.
#
# Entries are of the form:
# $ORACLE_SID:$ORACLE_HOME:<N|Y>:
#
# Multiple entries with the same $ORACLE_SID are not allowed.
#
#
test11:/u01/app/oracle/product/11.2.0/dbhome_1:N
orcl:/u01/app/oracle/product/10.2.0/db_1:N
End of Cat Output
From the above file am trying to extract the STRING before the :/
As a start, try this:
$ cat input.txt
test11:/u01/app/oracle/product/11.2.0/dbhome_1:N
orcl:/u01/app/oracle/product/10.2.0/db_1:N
$ awk -F: '{print $1}' input.txt
test11
orcl
update
Using bash:
#!/bin/bash
ARRAY=()
while read -r line
do
[[ "$line" = \#* ]] && continue
data=$(awk -F: '{print $1}' <<< $line)
ARRAY+=($data)
done < input.txt
for i in "${ARRAY[#]}"
do
echo "$i"
done
In action:
$ ./db.sh
test11
orcl
You could use sed also,
sed -r 's/^([^:]*):.*$/\1/g' file
Example:
$ cat cc
test11:/u01/app/oracle/product/11.2.0/dbhome_1:N
orcl:/u01/app/oracle/product/10.2.0/db_1:N
$ sed -r 's/^([^:]*):.*$/\1/g' cc
test11
orcl
OR
$ sed -nr 's/^(.*):\/.*$/\1/p' file
test11
orcl