Splitting up a variable in bash

172 Views Asked by At

I have a bunch of songs that I have downloaded that I am trying to convert to .mp3 and rename. After converting the .m4a file to mp3 with ffmpeg, they are all in the format of Artist Name - Song Name.mp3. I want to be able to separate the Song name and Artist name into their own variables, so I can rename the file and tag the songs with the song name and artist name.

So, How can I separate a variable into two variables, each respectively containing the artist name and song name, where the two pieces of information is separated by a ' - ' in bash?

3

There are 3 best solutions below

4
On BEST ANSWER

Using shell

v='Artist Name - Song Name.mp3'
v=${v%.mp3}
song=${v#* - }
artist=${v% - $song}
echo a=$artist, s=$song

This produces the output:

a=Artist Name, s=Song Name

Notice that this approach, like the sed solution below, consistently divides the artist and song at the first occurrence of -. Thus, if the name is:

v='Artist Name - Song Name - 2nd Take.mp3'

Then, the output is:

a=Artist Name, s=Song Name - 2nd Take

This approach is POSIX and works under dash and busybox as well as under bash.

Using sed

$ v='Artist Name - Song Name.mp3'
$ { read -r artist; read -r song; } < <(sed 's/.mp3$//; s/ - /\n/' <<<"$v")
$ echo a=$artist s=$song
a=Artist Name s=Song Name

This assumes (1) the first occurrence of - divides the artist name from the song name, and (2) the file name, $v, has only line (no newline characters).

We can overcome the second limitation by using, if your tools support it, NUL as the separator instead of newline:

$ { read -r -d $'\0' artist; read -r -d $'\0' song; } < <(sed 's/.mp3$//; s/ - /\x00/' <<<"$v"); echo a=$artist s=$song
a=Artist Name s=Song Name

Here is an example with newline characters inside both the artist and song names:

$ v=$'Artist\nName - Song\nName.mp3'
$ { read -r -d $'\0' artist; read -r -d $'\0' song; } < <(sed 's/.mp3$//; s/ - /\x00/' <<<"$v"); echo "a=$artist"; echo "s=$song"
a=Artist
Name
s=Song
Name

How it works

The sed command removes the .mp3 suffix and then replaces - with a newline character:

sed 's/.mp3$//; s/ - /\n/' <<<"$v"

The output of the sed command consists of two lines. The first line has the artist name and the second the song name. These are then read by the two read commands.

0
On

It can be done with pure bash:

#!/bin/bash
name="Artist Name - Song Name.mp3"
songname="${name#*- }"
songnamewithoutextentions="${songname%.*}"
artistname="${name% -*}"
printf "%s\n%s\n%s\n" "$artistname" "$songname" "$songnamewithoutextentions"

Output:

Artist: Artist Name
Song: Song Name.mp3
Song name without extention: Song Name

Explanation:

"${name#*- }" is the string after the first match of - in $name

"${name% -*}" is the string before the last match (first match from right) of - in $name

You can know more about Bash parameter expansion from the manual.

1
On

Use regular expression matching built into bash:

regex='(.*) - (.*).mp3'
[[ $v =~ $regex ]]
artist=${BASH_REMATCH[1]}
song=${BASH_REMATCH[2]}