Once you install FsQuass, it can work as command line tool. It’s almost like GNU find, except that it only searches and does not try to be a Swiss knife. There are no options in it:
$ fsquass '/home/*'
/home/siberiano
/home/guest
note: | It’s better to quote the arguments since Bash may try to convert masks (*, .) for you. |
---|
If you need to do something with the found files, use xargs:
$ fsquass '/home .bashrc' | xargs cat
This prints the contents of all .bashrc files Descendants of user folders. As a quick tip on xargs, to pass file path in the middle of a command, use curly braces:
$ fsquass '/home .bashrc' | xargs -I '{}' ln -S {} /tmp
To each found .bashrc this will make a symlink in /tmp.
The Fs class (stands for files set), like jQuery, searches by string and also inherits the API of set class with all set operations: union, intersection, add, remove, etc.
from fsquass import Fs
Fs('/home .bashrc') - Fs('~/.bashrc') # similar to jQuery.not()
File sets are iterable and consist of File or Dir instances. They also can generate strings:
for project in Fs('~/projects/*'):
print project
for path in Fs('~/projects/*'): # a generator of string paths
print path
The syntax is essentially Unix filename patterns + some powerful extensions. Patterns work via fnmatch module. The special characters used in shell-style wildcards are: *, ? (any single character), [abc] (a, b or c), [!abc] anything but them. Some simple examples:
folder/folder/file.py[co]
folder/*/*.txt
/etc/hosts
/var/log/*.log
./file
file
../another_folder/file
The two latter examples are the same.
But here come some extensions: you can go a level up from a file:
fsquass/setup.py/..
This expression will evaluate to fsquass, but only if setup.py is present. This is useful if you need folders to contain specific children.
It works like in CSS:
~ *.py
will search for *.py anywhere in the home folder, at any folders depth.
attention: | This kind of search is expensive since it makes the program go through all the directory tree down from ~. Make such searches as narrow as possible if you can: */projects/django *.py. |
---|
You can make several such searches:
~/projects templates *.haml
Scans projects folder for templates, then scans each of those for *.haml.
The second part of descendant can be multi-level:
~/projects templates/*.haml
Use backslash to write a space in a name. Use double backslash if you need to escape the backslash itself:
~/project\ description/*
Yet there is no syntax for the opposite search, for random number of levels upwards.
Similar to those in CSS, they are written in the end or instead of a pattern, and either filter filesystem objects by type, or modify the pattern’s properties:
Pseudo-Class | Meaning |
---|---|
:file | object is a file |
:dir | object is a directory |
:ignorecase | makes search case-insensitive |
Examples:
~/.*:dir
~/*:file
/home/:dir
/:dir
~/Pictures *.jpg:ignorecase
In Unix shell, you can do this: mkdir project/{apps,templates,static}. The same works in fsquass. Between slashes, you can use curly braces to write multiple options:
/home/{siberiano,guest}/.bashrc
~/Pictures {*.jpeg:ignorecase,*.jpg:ignorecase}
note: | Pseudo-classes must be inside the curly braces. |
---|
If you need to find objects with completely different paths or patterns, write multiple expressions separated with a colon:
/etc/hosts;~/.my_hosts;~/test.txt
Put a backslash to a colon if it’s a part of a name:
strange\;name1;strange\;name2
Fs inherits from set and suspports all the set methods.
Fs('./*.py') | Fs('./*.pyc') # union
Fs('. {*.rst,*.txt}') - Fs('./build *.txt') # not
Fs('*.py') & Fs('__*__.py') # intersection
Fs('*.py') ^ Fs('__*__.py') # xor (union not intersection)
filter is just like interection, but is faster since it doesn’t search files on disk.
Fs('*.py').filter('__*__.py')
Having one set of files you can generate another set relative of it:
# find Python scripts and then their parent folders that start with 'django'
django_projects = Fs('~/projcets *.py').parents('django*')
# inside those find __init__.py at top level
django_projects.children('__init__.py')
# or at any depth
django_projects.find('. __init__.py')
# find doc roots inside them, by relative path (no recursive search)
django_projects.find('docs/source/index.*')
django_projects.siblings()