ZSH Expansion

Easily find files

Created by Matthew Franglen

What's the problem?

Bash has pattern expansion

except you can't search all folders...

and you can't exclude files from the results...

and you can't match by file attributes.

ZSH Expansion can do that

In Bash you end up using the find command.

In ZSH you don't have to!

Extended Globbing

setopt extended_glob

Most of the patterns in this talk require extended globbing

Set this in your .zshrc!

Basic Matching

* will match any file or folder

? will match any single letter

[abc] will match any letter in the list

Recursive Matching

**/ will match any contained folder


% ls -d **/
css/        css/shaders/  css/theme/source/    js/   lib/css/   ...
css/print/  css/theme/    css/theme/template/  lib/  lib/font/  ...
                        

It really matches zero or more folders

Exclusion

PATTERN~EXCLUSION

Anything matching the exclusion will not be returned


% ls *.*
grunt.js  index.html  package.json  README.md  REVEAL.md

% ls *.*~*.md
grunt.js  index.html  package.json
                        

Exclude Results Not Folders

It just removes the individual excluded paths from the result

Multiple Exclusions Work

Just apply them one after the other


% ls **/*.js~plugin/**/*.js~lib/**/*.js
grunt.js  js/reveal.js  js/reveal.min.js
                        

File Attributes

PATTERN~EXCLUSION(ATTRIBUTES)

Allows you to restrict the match by things other than filename

File Type

. will match only files

/ will match only directories

@ will match only symlinks

^ will reverse the match


% ls *(.)
grunt.js  index.html  LICENSE  package.json  README.md ...

% ls -d *(^.)
css  js  lib  plugin
                        

Lots More!

You can match by file permissions...

You can match by owner...

You can match by modification date...

Check the zshexpn manual Glob Qualifiers section for more!

Result Alteration

PATTERN~EXCLUSION(ATTRIBUTES ALTERATIONS)

Allows you to alter the results to match your desired use

Path Parts

:t will remove the leading directories from the path

:h will remove the last file or directory from the path

:e will remove everything but the file extension

:r will remove the file extension


% echo index.html(:t) index.html(:h) index.html(:e) index.html(:r)
index.html . html index

% echo css/print/paper.css(:t:r)
paper
                        

Substitution

:s#FIND#REPLACE# will replace the first match

:gs#FIND#REPLACE# will replace all matches


% echo css/print/paper.css(:s#css#woo#)
woo/print/paper.css

% echo css/print/paper.css(:gs#css#woo#)
woo/print/paper.woo
                        

Works on Variables too!

Any variable can have alterations applied to it


% a="Looks like a /path/to.me"
% echo $a:h $a:t:r ${a:e}
Looks like a /path to me
                        

Save Patterns for later

You can expand variables as if they were patterns

This can be useful for building queries


% a='*(.)'
% ls $~a
grunt.js  index.html  LICENSE  package.json  README.md ...
                        

Remember to single quote the pattern to avoid early expansion!

Changing Text Matching

(#FLAG)

Globbing Flags change the way text in your pattern matches files.

They can be placed anywhere in a pattern and take effect from that point on.

Fuzzy Matching

(#aFACTOR)

Much like many search engines, you can apply a fuzz factor to your pattern.

The factor allows for letter replacement, swapping, addition, and removal.


% ls (#a1)gwunt.js (#a3)git.js
grunt.js  grunt.js
                        

Case Insensitive Matching

(#i)

You can apply Globbing Flags to part of a pattern using brackets.


% ls (#i)readme.MD
README.md

% ls ((#i)readme).MD
zsh: no matches found: ((#i)readme).MD
                        

Writing your own Matcher

If you need to match in a way not covered here,

Or if you just want to use a more familiar tool

Inline Matcher

e:'CONDITION':

Executes the condition as shell code.

The current match to test is stored in $REPLY


% ls **/*(e:'[ $(mimetype -b $REPLY) = "text/x-markdown" ]':)
css/theme/README.md  README.md  REVEAL.md
                        

Remember to single quote the condition to prevent early expansion!

Function Matcher

+FUNCTION NAME

Allows you to use a function for more complex conditions.

If the function returns true then the current file will be matched.


% function file_matcher () {
    hash=$(openssl md5 $REPLY | cut -d' ' -f2)
    [ $hash = "9dcf19ac58cdcf7952ae9444671b0fa6" ]
}
% ls **/*(.+file_matcher)
js/reveal.js
                        

As you can see, you can apply other restrictions too!

Altering the Result

Assigning a new value to $REPLY will change the matched file.

But $reply is even better, as it can hold an array!


% function file_doubler () {
    reply=( $REPLY $REPLY ) # even assignment has a return value
}
% ls js/reveal.js(+file_doubler)
js/reveal.js  js/reveal.js
                        

You can also change the result by applying the built in alterations after the matcher

The End

All rights reserved

Do not distribute without permission

Github and Github Pages are permitted to distribute this