MarkoJ
MarkoJ

Reputation: 19

Combine output of two grep results without spaces in between

I have a file:

...
version=1
...
subversion=2
..

I wish to display "version.subversion", so in current case "1.2".

What I currently have looks like this:

echo `echo -n $(grep "version=" file | awk -F= '{print $2 "."}'); grep -n "subversion=" file | awk -F= '{print $2}'`

So I have two separate greps following each other and the first one is wrapped into echo -n to get rid of the space that I would otherwise have between them.

This works, but doesn't look ideal. Is there a better way to do this?

Upvotes: 0

Views: 708

Answers (6)

Ivan
Ivan

Reputation: 7253

In some cases source can do the job, for this test file:

$ cat file.txt 
ls
some text
version=1
subversion=2
echo ok

$ ver=$(. file.txt &> /dev/null; echo "${version:-0}.${subversion:-0}")
$ echo $ver
1.2

Sourcing done in subshell so it won't change current env. All output and errors thrown to /dev/null and echoed only needed info.

Upvotes: 0

Benjamin W.
Benjamin W.

Reputation: 52102

If you know for sure that the version line comes first, you can do something like this:

$ grep -E '^(sub)?version=' infile | cut -d= -f2 | paste -sd.
1.2

which works as follows:

grep -E '^(sub)?version=' infile | # Get lines starting with "version"/"subversion"
    cut -d= -f2 |                  # Print part after "="
    paste -sd.                     # Join lines, separated by "."

And if the order is not deterministic, we can add a (reverse) sort step:

grep -E '^(sub)?version=' infile | sort -r | cut -d= -f2 | paste -sd.

Upvotes: 4

James Brown
James Brown

Reputation: 37394

Lol, so I wrote this awk and then noticed that almost every feature was in another answer (++) except quick exit once done and if version or subversion is missing, 0 is printed instead (ie. 1.0 or 0.2):

$ awk -F\= '                             # set field delimiter to =
$1~/^(sub)?version$/ {                   # only process sub- or version fields 
    a[$1]=$2                             # hash key and value
    if(a["version"] && a["subversion"])  # once both met
        exit                             # no more need to process so exit
}
END {                                    # in end or after exit
    print ((v=a["version"])?v:0) "." ((v=a["subversion"])?v:0)  # output value or 0
}' file

Output:

1.2

or:

1.0  # if missing subversion

or:

0.2  # if missing version

Upvotes: 1

Paul Hodges
Paul Hodges

Reputation: 15246

I also used awk for a one-pass, one-proc solution. I quit after subversion to avoid wasted I/O and cycles, though this is likely a small enough task that it doesn't matter - just like to stay in the habit of being as efficient as possible in case the paradigm changes a little sometime in the future.

 awk -F= '/^version/{v=$2}; /^subversion/{ printf "%d.%d\n",v,$2; quit; }' file

Upvotes: 0

John Kugelman
John Kugelman

Reputation: 361546

Let's simplify it one step at a time.

  1. The outer echo `...` can go. Backticks and echo are basically inverse operations and they (more or less) cancel out when combined.

    echo -n "$(grep "version=" file | awk -F= '{print $2 "."}')"
    grep -n "subversion=" file | awk -F= '{print $2}'
    
  2. An easier way to combine the two lines is to embed them inside a single printout.

    echo "$(grep "version=" file | awk -F= '{print $2}').$(grep "subversion=" file | awk -F= '{print $2}')"
    
  3. Awk can both search and print. No need for grep.

    echo "$(awk -F= '$1=="version" {print $2}' file).$(awk -F= '$1=="subversion" {print $2}' file)"
    
  4. You could make this easier to read by using printf to split it across multiple lines:

    printf '%s.%s\n' \
        "$(awk -F= '$1=="version" {print $2}' file)" \
        "$(awk -F= '$1=="subversion" {print $2}' file)"
    

That looks pretty good. But you know what? This screams for a one-pass solution. Awk is a pretty capable mini-language in its own right. We could have it save all the key/value pairs into a map and then print the results at the end:

awk -F= '{map[$1]=$2} END {print map["version"] "." map["subversion"]}' file

Here {map[$1]=$2} is executed for each line of the input file, saving the keys and values to a map. When the file is finished the END block runs and prints the two desired fields with a . in between.

Upvotes: 4

markp-fuso
markp-fuso

Reputation: 33854

One awk solution:

$ awk -F"=" '/^version/ {v=$2;next} /^subversion/ {sv=$2} END {printf "%s.%s\n",v,sv}' file
1.2

Where:

  • -F"=" - input field separator
  • /^*version/ - search patterns
  • v/sv=$2 - store the 2nd field
  • next - optional; skips to next input line
  • END/printf - output the results as v.sv

Upvotes: 1

Related Questions