Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Split 1 argument into 2 arguments using regexp in a bash script

Tags:

regex

bash

Here's my situation. Currently, I have a script that accepts two arguments: book name and chapter name. For example:

$ myscript book1 chap1

Now, for reasons that would take a long time to explain, I would prefer my script to be able to take a single argument of the following format: {book name}.{chapter name}. For example:

$ myscript book1.chap1

The difficulty for me is that I do not know how to take a string $1=abc.xyz and turn it into two separate variables, $var1=abc and $var2=xyz. How can I do this?

like image 205
synaptik Avatar asked Jul 10 '12 14:07

synaptik


3 Answers

If it's just two tags you can use a bash expression

arg=$1
beforedot=${arg%.*}
afterdot=${arg#*.}

It's faster than cut because it's a shell builtin. Note that this puts everything before the first last dot into beforedot and everything after into afterdot.

EDIT:

There's also a substitution/reinterpretation construct if you want to split by an arbitrary number of tokens:

string=a.b.c.d.e
tokens=(${string//\./ })

You're replacing dots by spaces and then that gets interpreted as an array declaration+definition because of the parentheses around it.

However I've found this to be less portable to bash' siblings and offspring. For example, it doesn't work in my favourite shell, zsh.

Arrays need to be dereferenced with braces and are indexed from 0:

echo "Third token: ${tokens[2]}"

You can loop through them as well by dereferencing the whole array with [@]:

for i in ${tokens[@]}
do
    # do stuff
done
like image 55
smocking Avatar answered Oct 20 '22 00:10

smocking


For completeness and since you asked about a regex method:

pattern='^([^.]*)\.(.*)'
[[ $1 =~ $pattern ]]
book=${BASH_REMATCH[1]}
chapter=${BASH_REMATCH[2]}

The capture groups are elements in the BASH_REMATCH array. Element 0 contains the whole match.

This regex will capture up to the first dot in the first element. Anything after the first dot including susbsequent dots will be in the second element. The regex can be easily modified to break on the last dot if needed.

like image 37
Dennis Williamson Avatar answered Oct 19 '22 23:10

Dennis Williamson


If $arg contains book.chap

read BOOK CHAP<<<$(IFS="."; echo $arg)

will set the variables BOOK and CHAP accordingly. This uses the bash internal field separator (IFS) which controls how bash understands word boundaries. If (say) you have multiple separators in your original $arg then just specify further variables to contain the results.

From here:

$IFS defaults to whitespace (space, tab, and newline), but may be changed, for example, to parse a comma-separated data file

like image 38
Brian Agnew Avatar answered Oct 19 '22 23:10

Brian Agnew