How can I extract a version string from a line and save it to a variable, using bash? - bash

I am new to scripting and I have a task which consists of extracting a version from a LOC and save it to $VERSION. The line looks like this:
#define PROJECT_VERSION "21.02"
I have to extract "21.02" and save it in $VERSION, using bash.
Right now, I am using the following command:
awk '/PROJECT_VERSION/{print $NF}' common/ESUtils.cpp
but it return more strings. Can you help me?

You can also consider using
awk '$2 == "PROJECT_VERSION"{print $3; exit}' common/ESUtils.cpp
That will find the first record wih Field 2 equal to PROJECT_VERSION and output Field 3 value, and exit right after it without looking further in the input file.
See an online demo:
#!/bin/bash
s='#define PROJECT_VERSION "21.02"
1 PROJECT_VERSION "22.02"'
awk '$2 == "PROJECT_VERSION"{print $3; exit}' <<< "$s"
# => "21.02"

Using sed:
VERSION=$(sed -En '/PROJECT_VERSION/s/(^.*\")([[:digit:]]+\.[[:digit:]]+)(\".*$)/\2/p' project.cpp)
Enable regular expression interpretation with -E and then search for the line with "PROJECT_VERSION". With this line, split the line into 3 sections specified in parenthesis and substitute the line for the second section only, printing the result.

The answer would be:
VERSION=`awk '/PROJECT_VERSION/{print $3F}' project.cpp

Related

Print part of a comma-separated field using AWK

I have a line containing this string:
$DLOAD , 123 , Loadcase name=SUBCASE_1
I am trying to only print SUBCASE_1. Here is my code, but I get a syntax error.
awk -F, '{n=split($3,a,"="); a[n]} {printf(a[1]}' myfile
How can I fix this?
1st solution: In case you want only to get last field(which contains = in it) then with your shown samples please try following
awk -F',[[:space:]]+|=' '{print $NF}' Input_file
2nd solution: OR in case you want to get specifically 3rd field's value after = then try following awk code please. Simply making comma followed by space(s) as field separator and in main program splitting 3rd field storing values into arr array, then printing 2nd item value of arr array.
awk -F',[[:space:]]+' '{split($3,arr,"=");print arr[2]}' Input_file
Possibly the shortest solution would be:
awk -F= '{print $NF}' file
Where you simply use '=' as the field-separator and then print the last field.
Example Use/Output
Using your sample into in a heredoc with the sigil quoted to prevent expansion of $DLOAD, you would have:
$ awk -F= '{print $NF}' << 'eof'
> $DLOAD , 123 , Loadcase name=SUBCASE_1
> eof
SUBCASE_1
(of course in this case it probably doesn't matter whether $DLOAD was expanded or not, but for completeness, in case $DLOAD included another '=' ...)

Prepend text to specific line numbers with variables

I have spent hours trying to solve this. There are a bunch of answers as to how to prepend to all lines or specific lines but not with a variable text and a variable number.
while [ $FirstVariable -lt $NextVariable ]; do
#sed -i "$FirstVariables/.*/$FirstVariableText/" "$PWD/Inprocess/$InprocessFile"
cat "$PWD/Inprocess/$InprocessFile" | awk 'NR==${FirstVariable}{print "$FirstVariableText"}1' > "$PWD/Inprocess/Temp$InprocessFile"
FirstVariable=$[$FirstVariable+1]
done
Essentially I am looking for a particular string delimiter and then figuring out where the next one is and appending the first result back into the following lines... Note that I already figured out the logic I am just having issues prepending the line with the variables.
Example:
This >
Line1:
1
2
3
Line2:
1
2
3
Would turn into >
Line1:
Line1:1
Line1:2
Line1:3
Line2:
Line2:1
Line2:2
Line2:3
You can do all that using below awk one liner.
Assuming your pattern starts with Line, then the below script can be used.
> awk '{if ($1 ~ /Line/ ){var=$1;print $0;}else{ if ($1 !="")print var $1}}' $PWD/Inprocess/$InprocessFile
Line1:
Line1:1
Line1:2
Line1:3
Line2:
Line2:1
Line2:2
Line2:3
Here is how the above script works:
If the first record contains word Line then it is copied into an awk variable var. From next word onwards, if the record is not empty, the newly created var is appended to that record and prints it producing the desired result.
If you need to pass the variables dynamically from shell to awk you can use -v option. Like below:
awk -v var1=$FirstVariable -v var2=$FirstVariableText 'NR==var{print var2}1' > "$PWD/Inprocess/Temp$InprocessFile"
The way you addressed the problem is by parsing everything both with bash and awk to process the file. You make use of bash to extract a line, and then use awk to manipulate this one line. The whole thing can actually be done with a single awk script:
awk '/^Line/{str=$1; print; next}{print (NF ? str $0 : "")}' inputfile > outputfile
or
awk 'BEGIN{RS="";ORS="\n\n";FS=OFS="\n"}{gsub(FS,OFS $1)}1' inputfile > outputfile

bash grep for string and ignore above one line

One of my script will return output as below,
NameComponent=Apache
Fixed=False
NameComponent=MySQL
Fixed=True
So in the above output, I am trying to ignore the below output using grep grep -vB1 'False' which seems not working,
NameComponent=Apache
Fixed=False
Is it possible to perform this using grep or is any better way with awk..
<some-command> |tac |sed -e '/False/ { N; d}' |tac
NameComponent=MySQL
Fixed=True
For every line that matches "False", the code in the {} gets executed. N takes the next line into the pattern space as well, and then d deletes the whole thing before moving on to the next line. Note: using multiple pipes is not considered as good practice.
#Karthi1234: If your Input_file is same as provided samples then try:
awk -F' |=' '($2 != "Apache" && $2 != "False")' Input_file
First making field separator as a space or = then checking here if field 2nd's value is not equal to sting Apache and False and mentioned no action to be performed so default print action will be done by awk.
EDIT: as per OP's request following is the code changed one, try:
awk '!/Apache/ && !/False/' Input_file
You could change strings too in case if these are not the ones which you want, logic should be same.
EDIT2: eg--> You could change values of string1 and string2 and increase the conditions if needed as per your requirement.
awk '!/string1/ && !/string2/' Input_file
If I understand the question correctly you will always have a line before "Fixed=..." and you want to print both lines if and only if "Fixed=True"
The following awk should do the trick:
< command > | awk 'BEGIN {prev='NA'} {if ($0=="Fixed=True") {print prev; print $0;} prev=$0;}'
Note that if the first line is "Fixed=True" it will print the string "NA" as the first line.

awk: print first line of file before reading lines

How would I go about printing the first line of given input before I start stepping through each of the lines with awk?
Say I wanted to run the command ps aux and return the column headings and a particular pattern I'm searching for. In the past I've done this:
ps aux | ggrep -Pi 'CPU|foo'
Where CPU is a value I know will be in the first line of input as it's one of the column headings and foo is the particular pattern I'm actually searching for.
I found an awk pattern that will pull the first line:
awk 'NR > 1 { exit }; 1'
Which makes sense, but I can't seem to figure out how to fire this before I do my pattern matching on the rest of the input. I thought I could put it in the BEGIN section of the awk command but that doesn't seem to work.
Any suggestions?
Use the following awk script:
ps aux | awk 'NR == 1 || /PATTERN/'
it prints the current line either if it is the first line in output or if it contains the pattern.
Btw, the same result could be achieved using sed:
ps aux | sed -n '1p;/PATTERN/p'
If you want to read in the first line in the BEGIN action, you can read it in with getline, process it, and discard that line before moving on to the rest of your awk command. This is "stepping in", but may be helpful if you're parsing a header or something first.
#input.txt
Name City
Megan Detroit
Jackson Phoenix
Pablo Charlotte
awk 'BEGIN { getline; col1=$1; col2=$2; } { print col1, $1; print col2, $2 }' input.txt
# output
Name Megan
City Detroit
Name Jackson
City Phoenix
Name Pablo
City Charlotte
Explaining awk BEGIN
I thought I could put it in the BEGIN section ...
In awk, you can have more than one BEGIN clause. These are executed in order before awk starts to read from stdin.

searching multi-word patterns from one file in another using awk

patterns file:
wicked liquid
movie
guitar
balance transfer offer
drive car
bigfile file:
wickedliquidbrains
drivelicense
balanceofferings
using awk on command line:
awk '/balance/ && /offer/' bigfile
i get the result i want which is
balanceofferings
awk '/wicked/ && /liquid/' bigfile
gives me
wickedliquidbrains, which is also good..
awk '/drive/ && /car/' bigfile
does not give me drivelicense which is also good, as i am having &&
now when trying to pass shell variable, containg those '/regex1/ && /regex2/.. etc' to awk..
awk -v search="$out" '$0 ~ search' "$bigfile"
awk does not run.. what may be the problem??
Try this:
awk "$out" "$bigfile"
When you do $0 ~ search, the value of search has to be a regular expression. But you were setting it to a string containing a bunch of regexps with && between them -- that's not a valid regexp.
To perform an action on the lines that match, do:
awk "$out"' { /* do stuff */ }' "$bigfile"
I switched from double quotes to single quotes for the action in case the action uses awk variables with $.
UPDATED
An alternative to Barmars's solution with arguments passed with -v:
awk -v search="$out" 'match($0,search)' "$bigfile"
Test:
$ echo -e "one\ntwo"|awk -v luk=one 'match($0,luk)'
one
Passing two (real) regexs (EREs) to awk:
echo -e "one\ntwo\nnone"|awk -v re1=^o -v re2=e$ 'match($0,re1) && match($0,re2)'
Output:
one
If You want to read the pattern_file and do match against all the rows, You could try something like this:
awk 'NR==FNR{N=NR;re[N,0]=split($0,a);for(i in a)re[N,i]=a[i];next}
{
for(i=1;i<=N;++i) {
#for(j=1;j<=re[i,0]&&match($0,re[i,j]);++j);
for(j=1;j<=re[i,0]&&$0~re[i,j];++j);
if(j>re[i,0]){print;break}
}
}' patterns_file bigfile
Output:
wickedliquidbrains
At the 1st line it reads and stores the pattern_file in a 2D array re. Each row contains the split input string. The 0th element of each row is the length of that row.
Then it reads bigfile. Each lines of bigfile are tested for match of re array. If all items in a row are matching then that row is printed.

Resources