ZSH globbing to play music

Tags: shell, zsh, linux, music

Too lazy to learn to use the awesome cmus, and living on an (very regrettably) extremely unorganized music library since I came to IIT Kanpur, yeah that’s me these days when it comes to music. Without the likes of Spotify in India, I’ve been living on YouTube since more than 2 years, when I used to have a fully organized music library back in my school days.

Having a very dynamic (and mostly incomplete) music library, I was constantly being annoyed having to import folders, sort some, organize others. What would be ideal in such a scenario is a way to live in the shell itself, but have the freedom to easily search for music files and play them.

If you’re not familiar with pattern globbing, fire up ZSH and try this:

cd Pictures
ls **/*.jpg

Nice right? The ** stands for recursive searching. So it will go and search recursively in all subfolders of the current directory.

zsh-play

I envisioned an interface of this sort:

play river flows

This ought to open all songs containing the words river and flows in their name, in this particular order, regardless of the case of the spellings. Also, it shouldn’t be matching all kinds of files, so I added a -t flag, to decide the file type, set to mp3 by default, but something like -t flac works as well.

How to?

The code was short enough. I added a new ZSH function in my ZSH configuration, and voila! In reality though, it took me a great while to make it work, because I did not know (sounds obvious in hindsight as always) that globs (The useful *s) are not expanded when coming from a string variable in the shell. And why should they?

You can pick the code from the end of this article, and use it by putting it in your .zshrc and sourcing it. Voila!

Lessons

  • Globs aren’t expanded from strings. Don’t hope to put strings containing *s in a command, and expect it to expand. Use {~var} instead.
  • Functions in ZSH are quite powerful! Better use them instead of writing aliases and small scripts.
  • getopts and OPTARGS is a really nice way to implement CLI arguments!
  • You need setopt extendedglob for some globbing to work.
  • Case insensitive matching of globs uses (#i).
  • You can generate array of names of files very easily with list=(**/*.mp3) kind of syntax.

The code:

function play() {
    EXT="mp3"
    while getopts “ht:r:p:v” OPTION
    do
        case $OPTION in
            t)
                EXT=$OPTARG
                echo "$EXT extension"
                shift 2
                ;;
            ?)
            usage
            ;;
        esac
    done

    setopt extendedglob
    pat=$(echo -n "[email protected]" | sed -E 's/\s+/\*/g')
    list=((#i)**/*${~pat}*.${EXT})
    vlc $list
}
Author's picture

Saksham Sharma

Hacker. Programmer. Geek.

3rd year Undergrad, Computer Science and Engineering

Indian Institute of Technology Kanpur