0

I have this function, works fine, but I would like to rewrite it in bash. the problem is, I have too little knowledge of what's available in bash.

#!/usr/bin/python

def parse_svnversion(value):
    """split the output of svnversion into its three components

    given a string that looks like the output of the command
    svnversion, returns the 3-tuple (low, high, flags)

    >>> parse_svnversion('1024')
    (1024, 1024, '')
    >>> parse_svnversion('1024:2000')
    (1024, 2000, '')
    >>> parse_svnversion('1024M')
    (1024, 1024, 'M')
    >>> parse_svnversion('1024:2000MP')
    (1024, 2000, 'MP')
    """

    values = filter(lambda x: x.isdigit() or x==':', value).split(':')
    return int(values[0]), int(values[-1]), filter(str.isalpha, value)

if __name__ == '__main__':
    import doctest
    doctest.testmod()

what I would like is a similarly small bash function that I can invoke and that will set something (three variables? an array?) that I can use. if it's an array, I would really like it to be of fixed size (3).

3 Answers 3

4

This creates an array called "tuple" with three elements:

[[ $(svnversion .) =~ ([0-9]+):*([0-9]*)([A-Z]*) ]]
tuple[0]=${BASH_REMATCH[1]}
tuple[1]=${BASH_REMATCH[2]:-${tuple[0]}}
tuple[2]=${BASH_REMATCH[3]:-''}

Requires Bash 3.2 or greater. It may work in Bash >= 3 and < 3.2. Not portable to the Bourne shell, although it can be adapted for the Korn shell or the Z shell.

ksh uses the .sh.match array variable, for example: ${.sh.match[1]}

zsh uses the match array variable, for example: ${match[1]} or you can do

setopt bashrematch ksharrays

to have it work with the Bash version exactly as above.

The brace substitutions should be the same for all three.

Sign up to request clarification or add additional context in comments.

4 Comments

+1. I knew about the =~ operator but never knew that bash could do essentially back references with $BASH_REMATCH[@]. Might be worth mentioning that this script is non-portable and requires Bash 3 or greater.
I knew it was possible to do it in a compact way! thanks.
According to the Bash Changelog cnswww.cns.cwru.edu/~chet/bash/CHANGES , Chet added the =~ operator along with BASH_REMATCH[@] in 3.0-alpha
That's true, but 3.2 had this change: "Quoting the string argument to the [[ command's =~ operator now forces string matching, as with the other pattern-matching operators." which is what I was referring to, but doesn't apply here. Bash 4 introduces shopt -s compat31 to affect this behavior.
1

you can use this subroutine

parsesvn(){
 toparse="$1"
 num=${toparse%%[A-Z]*}
 alpha=${toparse##*[0-9]}
 IFS=":"
 set -- $num
 for i in $@
 do
    printf "%s " $i
 done
 if [ ! -z "$alpha" ];then
    printf "%s" "$alpha"
 fi
}

# main #
var=$(parsesvn "1024:2000")
set -- $var
if [ "$1" -lt "$2" ];then
    echo "ok"
    greater=$2
else
    echo "LHS: $1 greater than RHS: $2"
fi
echo "greater is $greater"

4 Comments

the alpha=${toparse##*[0-9]} part is useful, thanks, but splitting the toparse into an array... I'd like guaranteed two elements there, low and high, even if they are equal.
what do you mean low and high? do you mean to check if the LHS number is smaller than RHS number?
yes, I want to give a warning to the user: "you're working with mixed revisions". and further I need to work with the highest revision number. if there are flags, I also want to warn the user about them.
i am only following closely, if not 100% what your Python code is doing. The best i can do, since i am not really familiar with svn, is add code to check for greater or lesser part, and get the greater number, and you can proceed from there. In the end, you still have to get familiar with a bit of shell programming if you are going to convert that Python code to shell.
0

The following solution stores the values into the array arr[ ] to match your original tuple as closely as possible. After the if-else-fi block you can do whatever you want with arr[0],arr[1], and arr[2]. I tried to match your post (and comment) as closely as possible. Also, I took the liberty to send the warning and notice messages to STDERR rather than STDOUT thinking you probably want to separate those.

#!/bin/bash

parse_svnversion()
{
    if [[ "$1" = *:* ]]; then
        arr[0]=${1%:*}
        arr[2]=${1//[0-9:]/}
        tmp_arr[1]=${1#*:}
        arr[1]=${tmp_arr[1]//${arr[2]}/}
    else
        arr[2]=${1//[0-9:]/}
        arr[0]=${1//${arr[2]}/}
        arr[1]=${arr[0]}
    fi

    echo ${arr[@]} 

    head_rev=$( (( ${arr[0]} > ${arr[1]} )) && echo ${arr[0]} || echo ${arr[1]} )
    echo "Notice: head revision is $head_rev" >&2

    if (( ${arr[1]} < ${arr[0]} )); then
        echo "Warning: you're working with mixed revisions" >&2
    fi
    if [[ -n ${arr[2]} ]]; then
        echo "Warning: there are flags" >&2
    fi
}

parse_svnversion "1024"
parse_svnversion "1024:2000"
parse_svnversion "1024M"
parse_svnversion "1024:2000MP"
parse_svnversion "2000:1024M"

Result without STDERR (sent to /dev/null)

$ ./svn_split.sh 2> /dev/null
1024 1024
1024 2000
1024 1024 M
1024 2000 MP
2000 1024 M

Result with STDERR

$ ./svn_split.sh
1024 1024
Notice: head revision is 1024
1024 2000
Notice: head revision is 2000
1024 1024 M
Notice: head revision is 1024
Warning: there are flags
1024 2000 MP
Notice: head revision is 2000
Warning: there are flags
2000 1024 M
Notice: head revision is 2000
Warning: you're working with mixed revisions
Warning: there are flags

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.