Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to apply two matching pattern transformations in one operation?

Tags:

bash

shell

I have a bash variable populated with a filename and path:

SONG="~/Music/Mine/Cool Title Bro.flac"

In my attempts to make tagging dramatically easier, I applied a bit of transformation to the variable to isolate the title:

echo "${SONG#\~/Music/Mine/}" # which prints: Cool Title Bro.flac

I know it's also possible to remove the suffix with ${SONG%%.flac}.

But is it possible to remove both the prefix and the suffix in a single operation?

This:

${SONG#\~/Music/Mine/%%.flac}

doesn't work presumably because it tries to match a literal %%.flac as part of the prefix. The reverse does not work (%%.flac#~/[...]), and I've even gone crazy and tried

${${SONG#~/Music/Mine/}%%.flac}

which also does not work.

This may be a prime example of over-engineering on my part, but it'd be excellent if there is a way to do this and I just haven't figured it out yet.

like image 294
VxJasonxV Avatar asked Jun 20 '12 08:06

VxJasonxV


3 Answers

echo "1:"
if [[ "$SONG" =~ \~/Music/Mine/(.*)\.flac ]] ; then SONG=${BASH_REMATCH[1]} ; fi
echo $SONG

echo "2:"
[[ "$SONG" =~ \~/Music/Mine/(.*)\.flac ]] && SONG=${BASH_REMATCH[1]}
echo $SONG

1 and 2 use bash regular expressions. The first example has the added advantage of being able to break into an else branch if your string doesn't match the format thats expected.* The second example is a bit cleaner. In both cases, if ${SONG} doesn't match the pattern, it is left unchanged.

But using awk or sed might be easier to understand. For example:

echo "3:"
SONG=$(echo "$SONG" | sed -r 's:~/Music/Mine/(.*)\.flac:\1:')
echo $SONG

[*] See DennisWilliamson's note below regarding using || to get an else branch.

like image 102
jedwards Avatar answered Nov 01 '22 02:11

jedwards


In some cases, bash's extended patterns are flexible enough to accomplish what you want. First, you have to turn them on:

shopt -s extglob

Then, you can specify a list of patterns that should be removed using parameter expansion:

echo ${SONG//@(*\/|.*)}

The extended pattern @(*\/|.*) matches either everything up to a / (which must be escaped, to avoid confusing it with part of the parameter substitution syntax), or a period and everything following it. The // indicates that each occurrence of the pattern should be substituted.

like image 41
chepner Avatar answered Nov 01 '22 01:11

chepner


No, not possible. Use two operations.

tmp="${SONG#\~/Music/Mine/}"; echo "${tmp%.flac}"

Well ok, it's possible, if you're nuts.

a="~/Music/Mine/Cool Title Bro.flac"
echo "${a:b=$(b=${a%${a#\~/Music/Mine/}};echo ${#b}):$(c=${a%.flac};echo ${#c})-b}"

You could also use regex grouping in a shell that supports it. Example in jedwards answer

It can also be done using ksh93 pattern grouping.

song='~/Music/Mine/Cool Title Bro.flac'; echo "${song/'~/Music/Mine/'+(*).flac/\1}"
like image 2
ormaaj Avatar answered Nov 01 '22 03:11

ormaaj