Extract data between delimiters from a Shell Script variable - bash

I have this shell script variable, var. It keeps 3 entries separated by new line. From this variable var, I want to extract 2, and 0.078688. Just these two numbers.
var="USER_ID=2
# 0.078688
Suhas"
These are the code I tried:
echo "$var" | grep -o -P '(?<=\=).*(?=\n)' # For extracting 2
echo "$var" | awk -v FS="(# |\n)" '{print $2}' # For extracting 0.078688
None of the above working. What is the problem here? How to fix this ?

Just use tr alone for retaining the numerical digits, the dot (.) and the white-space and remove everything else.
tr -cd '0-9. ' <<<"$var"
2 0.078688
From the man page, of tr for usage of -c, -d flags,
tr [OPTION]... SET1 [SET2]
-c, -C, --complement
use the complement of SET1
-d, --delete
delete characters in SET1, do not translate
To store it in variables,
IFS=' ' read -r var1 var2 < <(tr -cd '0-9. ' <<<"$var")
printf "%s\n" "$var1"
2
printf "%s\n" "$var2"
2
0.078688
Or in an array as
IFS=' ' read -ra numArray < <(tr -cd '0-9. ' <<<"$var")
printf "%s\n" "${numArray[#]}"
2
0.078688
Note:- The -cd flags in tr are POSIX compliant and will work on any systems that has tr installed.

echo "$var" |grep -oP 'USER_ID=\K.*'
2
echo "$var" |grep -oP '# \K.*'
0.078688
Your solution is near to perfect, you need to chance \n to $ which represent end of line.
echo "$var" |awk -F'# ' '/#/{print $2}'
0.078688
echo "$var" |awk -F'=' '/USER_ID/{print $2}'
2

You can do it with pure bash using a regex:
#!/bin/bash
var="USER_ID=2
# 0.078688
Suhas"
[[ ${var} =~ =([0-9]+).*#[[:space:]]([0-9\.]+) ]] && result1="${BASH_REMATCH[1]}" && result2="${BASH_REMATCH[2]}"
echo "${result1}"
echo "${result2}"

With awk:
First value:
echo "$var" | grep 'USER_ID' | awk -F "=" '{print $2}'
Second value:
echo "$var" | grep '#' | awk '{print $2}'

Assuming this is the format of data as your sample
# For extracting 2
echo "$var" | sed -e '/.*=/!d' -e 's///'
echo "$var" | awk -F '=' 'NR==1{ print $2}'
# For extracting 0.078688
echo "$var" | sed -e '/.*#[[:blank:]]*/!d' -e 's///'
echo "$var" | awk -F '#' 'NR==2{ print $2}'

Related

How to get version number from string in bash

I have a variable having following format
bundle="chn-pro-X.Y-Z.el8.x86_64"
X,Y,Z are numbers having any number of digits
Ex:
1.0-2 # X=1 Y=0 Z=2
12.45-9874 # X=12 Y=45 Z=9874
How can I grab X.Y and store it in another variable?
EDIT:
I wasn't right with my wording, but
I want to store X.Y into new variable not individual X & Y's
I'm looking to finally have a variable version which has X.Y grabbed from bundle:
version="X.Y"
I would use awk:
bundle="chn-pro-12.45-9874.el8.x86_64"
echo "$bundle" | awk -F "[.-]" '{print $3,$4,$5}'
12 45 9874
Now if you want to assign to x, y, z use read and process substitution:
read -r x y z < <(echo "$bundle" | awk -F "[.-]" '{print $3,$4,$5}')
echo "x=$x, y=$y, z=$z"
x=12, y=45, z=9874
If you just want the value of X.Y as a single value this is still great use for awk:
bundle="chn-pro-12.45-9874.el8.x86_64"
echo "$bundle" | awk -F "[-]" '{print $3}'
12.45
And if you then want to put that into a variable:
x_y=$(echo "$bundle" | awk -F "[-]" '{print $3}')
echo "x_y=$x_y"
x_y=12.45
Or you can use cut in this case to get the third field:
echo "$bundle" | cut -d- -f3
12.45
Like that:
$ bundle="chn-pro-1.0-2.el8.x86_64"
$ X="$(echo "$bundle" | cut -d . -f1 | cut -d- -f3)"
$ Y="$(echo "$bundle" | cut -d . -f2 | cut -d- -f1)"
$ Z="$(echo "$bundle" | cut -d . -f2 | cut -d- -f2)"
$ echo "$X"
1
$ echo "$Y"
0
$ echo "$Z"
2
You can merge X and Y into a single variable:
$ XY="$X.$Y"
$ echo $XY
1.0
Use regex to separate numbers:
numbers=$(echo $bundle | grep -Eo '([0-9]+\.[0-9]+\-[0-9]+)' | sed 's/\./\t/g;s/\-/\t/g')
Then assign them to variables with using awk or tr or cut, whatever you want:
X=$(echo $numbers| awk '{print $1}')
Y=$(echo $numbers| awk '{print $2}')
Z=$(echo $numbers| awk '{print $3}')
EDIT
For storing x.y into single version variable you can simply ignore pervios commands:
version=$(echo $bundle | grep -Eo '([0-9]+\.[0-9]+\-[0-9]+)' | grep -Eo '([0-9]+\.[0-9]+)')
Given this input:
$ bundle="chn-pro-12.45-9874.el8.x86_64"
using GNU or BSD sed for -E:
$ foo=$(echo "$bundle" | sed -E 's/.*-([0-9]+\.[0-9]+)-[0-9].*/\1/')
$ echo "$foo"
12.45
or with any sed:
$ foo=$(echo "$bundle" | sed 's/.*-\([0-9][0-9]*\.[0-9][0-9]*\)-[0-9].*/\1/')
$ echo "$foo"
12.45
Assumptions:
the input string will always contain (at least) 3 hyphens
the desired version string will always reside between the 2nd and 3rd hyphens of the input string
we need to maintain the input string (ie, don't clobber/overwrite the variable containing the input string)
We can eliminate the subprocess calls (necessary for echo/sed/grep/awk/sed) by using some parameter expansions:
$ bundle="chn-pro-X.Y-Z.el8.x86_64"
$ temp="${bundle#*-}" # strip off 1st hyphen delimited string
$ echo "${temp}"
pro-X.Y-Z.el8.x86_64
$ temp="${temp#*-}" # strip off 2nd hyphen delimited string
$ echo "${temp}"
X.Y-Z.el8.x86_64
$ version="${temp%%-*}" # save 3rd hyphen delimited string (aka our version)
$ echo "${version}"
X.Y
NOTE: We can eliminate the temp variable by replacing all occurrences of temp with version with the understanding version does not contain what we want until after the 3rd parameter expansion has occurred, eg:
$ bundle="chn-pro-X.Y-Z.el8.x86_64"
$ version="${bundle#*-}"
$ version="${version#*-}"
$ version="${version%%-*}"
$ echo "${version}"
X.Y

How to grab fields in inverted commas

I have a text file which contains the following lines:
"user","password_last_changed","expires_in"
"jeffrey","2021-09-21 12:54:26","90 days"
"root","2021-09-21 11:06:57","0 days"
How can I grab two fields jeffrey and 90 days from inverted commas and save in a variable.
If awk is an option, you could save an array and then save the elements as individual variables.
$ IFS="\"" read -ra var <<< $(awk -F, '/jeffrey/{ print $1, $NF }' input_file)
$ $ var2="${var[3]}"
$ echo "$var2"
90 days
$ var1="${var[1]}"
$ echo "$var1"
jeffrey
while read -r line; do # read in line by line
name=$(echo $line | awk -F, ' { print $1} ' | sed 's/"//g') # grap first col and strip "
expire=$(echo $line | awk -F, ' { print $3} '| sed 's/"//g') # grap third col and strip "
echo "$name" "$expire" # do your business
done < yourfile.txt
IFS=","
arr=( $(cat txt | head -2 | tail -1 | cut -d, -f 1,3 | tr -d '"') )
echo "${arr[0]}"
echo "${arr[1]}"
The result is into an array, you can access to the elements by index.
May be this below method will help you using
sed and awk command
#!/bin/sh
username=$(sed -n '/jeffrey/p' demo.txt | awk -F',' '{print $1}')
echo "$username"
expires_in=$(sed -n '/jeffrey/p' demo.txt | awk -F',' '{print $3}')
echo "$expires_in"
Output :
jeffrey
90 days
Note :
This above method will work if their is only distinct username
As far i know username are not duplicate

Get the third element of a line into a file with script shell

I'm doing a script shell and I want to read data inside a file. In the file, I have something like :
/path/to/file1 something 0
/path/to/file2 something2 1
/path/to/file3 something3 2
What I want is to get the third element of the line but I don't know how to do it.
In my code, I have:
while read line;
do
//must echo the third element of the line
done < file | sort -n -k 2 -t " "
I already tried with awk but it didn't work.
How should I do please ?
This works if fields are separated by space:
$ echo 'foo bar baz' | cut --delimiter=' ' --fields=3
baz
This works for most whitespace separators:
$ echo 'foo bar baz' | awk '{print $3}'
baz
you can try something like this;
while read line;
do
path=$(echo $line | awk '{print $1}')
secondColumn=$(echo $line | awk '{print $2}')
thirdColumn=$(echo $line | awk '{print $3}')
echo $path
echo $secondColumn
echo $thirdColumn
done < test

Need to generate files based on the value available in a variable in shell

In my script I have a variable $var which will hold a value "00135 00136 00137". I want to generate three files based on the values available in $var - if possible without using a loop.
For example, I need touch files with these names:
test.00136.txt
test.00137.txt
test.00138.txt
Avioding a while loop is possible with xargs.
First split the var into lines, use the string num as a placeholder and touch the files:
var="000135 00136 00137 00138 00139"
echo "${var}" | tr " " "\n" | xargs -I num touch test.num.txt
Edit:
Avoid tr with
echo -n "$var" | xargs -d' ' -n1 -Inum echo test.num.txt
The awk utility makes processing columnar data quite simple:
var="00135 00136 00137"
var1=$(echo "$var" | awk '{print $1}')
var2=$(echo "$var" | awk '{print $2}')
var3=$(echo "$var" | awk '{print $3}')
touch "test.${var1}.txt"
touch "test.${var2}.txt"
touch "test.${var3}.txt"

replacement for cut --output-delimiter

I created a script that was using
cut -d',' -f- --output-delimiter=$'\n'
to add a newline for each command separated value in RHEL 5, for e.g.
[root]# var="hi,hello how,are you,doing"
[root]# echo $var
hi,hello how,are you,doing
[root]# echo $var|cut -d',' -f- --output-delimiter=$'\n'
hi
hello how
are you
doing
But unfortunately when I run the same command in Solaris 10, it doesn't work at all :( !
bash-3.00# var="hi,hello how,are you,doing"
bash-3.00# echo $var
hi,hello how,are you,doing
bash-3.00# echo $var|cut -d',' -f- --output-delimiter=$'\n'
cut: illegal option -- output-delimiter=
usage: cut -b list [-n] [filename ...]
cut -c list [filename ...]
cut -f list [-d delim] [-s] [filename]
I checked the man page for 'cut' and alas there is no ' --output-delimiter ' in there !
So how do I achieve this in Solaris 10 (bash)? I guess awk would be a solution, but I'm unable to frame up the options properly.
Note: The comma separated variables might have " " space in them.
What about using tr for this?
$ tr ',' '\n' <<< "$var"
hi
hello how
are you
doing
or
$ echo $var | tr ',' '\n'
hi
hello how
are you
doing
With sed:
$ sed 's/,/\n/g' <<< "$var"
hi
hello how
are you
doing
Or with awk:
$ awk '1' RS=, <<< "$var"
hi
hello how
are you
doing
Perhaps do it in bash itself?
var="hi,hello how,are you,doing"
printf "$var" | (IFS=, read -r -a arr; printf "%s\n" "${arr[#]}")
hi
hello how
are you
doing

Resources