zsh tips: File picking

When you’re working on your shell, you immediately get in contact with files, directories and therefor paths. Most of the time there are simple paths, but sometimes it gets exciting and you need to defined multiple files. Instead of defining them manually on the shell, you can work with placeholders in form of asterisks.

Asterisks in file paths (e.g. data/*.txt) are called globs, and they’re really important for file picking. Globs are omnipresent in most modern shells, but because we’re talking about zsh, we can imagine that globs on zsh are on steroids as well!

Simple globs: File picking based on filenames

Of course you might already know simple globs like these:

# List all files / folders directly under foobar
ls -l foobar/*

# List all files / folders two levels under foobar
ls -l foobar/*/*

But with zsh you can go further:

# List all files / folders anywhere in foobar
ls -l foobar/**/*

# List all cfg files anywhere in foobar
ls -l foobar/**/*.cfg

# List all files / folders beginning with a-f anywhere in foobar
ls -l foobar/**/[a-f]*

# List all files ending with .cfg or .conf anywhere in foobar
ls -l foobar/**/*.(cfg|conf)

Glob qualifiers: File picking based on metadata

Glob qualifiers will help you to pick files/folders based on metadata (i.e. inode). If you get used to it, you’ll get more productive thanks to a really short syntax. This also means by using glob qualifiers, you can skip the use of the good old fashioned find:

# Show only directories under foobar
ls -l foobar/**/*(/)

# Show only files under foobar
ls -l foobar/**/*(.)

# Show only empty files under foobar
ls -l foobar/**/*(L0)

# Show only files < 100k under foobar
ls -l foobar/**/*(Lk-100)

# Show only files modified in the last 30 minutes
ls -l foobar/**/*(mm-30)

# Order files by modification date and show only 10 last recently modified files
ls -l foobar/**/*(om[1,10])


To keep it simple: If you get used to modifiers in zsh, then you can skip variable substitutions for paths, and you don’t even need to call dirname and basename in your scripts:

# Print the file name of all files (t=tail)
ls -l foobar/**/*(:t)

# Print all file names without extensions (r=remove extension)
ls -l foobar/**/*(:t:r)

# Print only extensions (e=extension)
ls -l foobar/**/*(:e)

# Print parent folders (h=head)
ls -l foobar/**/*(:h)

# Print parent folders of parent folders
ls -l foobar/**/*(:h:h)


Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.