Search code examples
bashgit-bash

How to write bash script that uses grep to get current Semantic Version, Update and Replacing?


I am working on automating the creation of release packages but am new to writing bash scripts.

I have a file that contains these lines showing the current version for the release:

#define VER_MAJOR   1
#define VER_MINOR   8
#define VER_BUILD   5

I need to write a script that can retrieve the three parts of the version number from a file. Increment the build number and then rewrite the new version numbers back to the file at the same line they were retrieved from.

I've described the script format below.

#Release Creation Script

#Retrieve Current Version 
VERSION_RAW_STRING='grep -n "#define VER_" version.h'

#Parse Line Numbers

#Parse Major/Minor/Build Numbers
VER_MAJOR=1
VER_MINOR=1
VER_BUILD=1

#Increment Build Number
VER_BUILD+=1

#Write Updated Version Back to main.cpp at Same Line Number

#Create Git Tag
$(git tag v${VER_MAJOR}.${VER_MINOR}.${VER_BUILD})

#Get Current Git Information
BUILD_TIME=`date +%FT%T%z`
GIT_REVISION=`git rev-parse --short HEAD`
GIT_BRANCH=`git rev-parse --symbolic-full-name --abbrev-ref HEAD`
GIT_DIRTY=`git diff-index --quiet HEAD -- || echo "✗-"`
GIT_TAG_DIRTY=`git describe --exact-match --tags > /dev/null 2> /dev/null || echo "+"`
GIT_TAG=`git describe --abbrev=0 --tags`

#Concatenate Git info and Echo
GITINFO="
buildTime = \"${BUILD_TIME}\"
gitRevision = \"${GIT_DIRTY}${GIT_REVISION}${GIT_TAG_DIRTY}\"
gitBranch = \"${GIT_BRANCH}\"
gitTag = \"${GIT_TAG}\""

echo "$GITINFO"

#Create Backup Package of Release
$(git archive -o ${GIT_TAG}.zip master)

The logic for when to increment the other version numbers is outside the scope of this question. I am really just trying to get a good understanding of how to read from files manipulate the outputs and write it back to a set location.

I currently am using grep to retrieve these lines and the line number they are on in the command line:

$ grep -n "#define VER_" main.cpp
98:#define VER_MAJOR    1
99:#define VER_MINOR    8
100:#define VER_BUILD   5

Now I am getting stuck on how to go about making this a script where I can then parse the return message to store the line numbers and the version numbers so that I can then increment them rebuild the string and replace them in the file


Solution

  • As noted by Charles Duffy, it may be a bad idea to mix automatic editing and human-maintained code.

    Also, note that grep is only a parsing program, it can not edit files on its own. You can still use grep for parsing MAJOR and MINOR versions, but since we'll use sed for editing the BUILD version, let's use it for everything (one dependency less!).

    A solution for your problem could be to use these commands (if you have GNU sed installed):

    # fetch the 3 versions
    VER_MAJOR=$(sed -nE 's/^#define VER_MAJOR[[:space:]]+([0-9]+)$/\1/p' main.cpp) 
    VER_MINOR=$(sed -nE 's/^#define VER_MINOR[[:space:]]+([0-9]+)$/\1/p' main.cpp)
    VER_BUILD=$(sed -nE 's/^#define VER_BUILD[[:space:]]+([0-9]+)$/\1/p' main.cpp)
    # now increment the BUILD version and write it back to file
    ((VER_BUILD++))
    sed -Ei 's/^(#define VER_BUILD[[:space:]]+)[0-9]+$/\1'$VER_BUILD/ main.cpp
    

    Explanations:

    • -E for extended regular expressions
    • -i tells sed to edit the file in place
    • -n and /p are used to only print what was substituted (no other lines)
    • The match string is pretty straightforward, but note the parentheses around the numbers.
      This defines a group: we replace the whole matched pattern (which is the whole line, since we used ^ and $ anchors) with \1 which is the first group (= the version number)

    And voilà, your VER_BUILD number is increased!


    For safety issues, you could prefer not editing in place (remove the -i from the last command and store stdout to another file >main_edited.cpp)