WAND is a Python CLI tool for creating a large number of directories from a single template and executing commands globally throughout every directory.
To install WAND simply run
pip install dir-wand
in the root directory of WAND. This will install the dir-wand
CLI.
WAND can efficiently make an arbitrary number of complex directory structures from a single template directory. This can be useful for creating a large number of directories with similar structures, such as for running many simulations, performance testing, or experiments. In the sections below we'll show how to create a template directory structure and use WAND to make copies of this structure with placeholders populated by a set of values.
A template directory can include any number of subdirectories and files. WAND will correctly handle the copying of:
- Empty directories.
- Executables with the correct permissions.
- Softlinks.
- Human readable text files (e.g.
.txt
,.yaml
,.csv
,.html
, etc.). - Binary files.
- And many more...
Templates can include any number of "placeholder" values in file paths and inside text files. A placeholder is defined by a set of curly braces containing the place holder label (e.g. {value1}
).
For example, your template directory might be:
└── simple_example_{num}/
├── simple_example_{num}.yaml
├── nested_dir/
│ ├── some_text.txt
│ └── another_nested_dir/
│ └── another_another_dir/
│ └── nested_file.txt
└── empty_dir/
where num
is one of the placeholders and we have a mixture of directories and files with and without placeholders in their name.
To include placeholders in a file we can again use the curly brace notation. For instance, in this example we have simple_example_{num}.yaml
which could contain:
PlaceHolders:
run_number: {num}
run_directory: simple_example_{num}
variable: {x}
another_variable: {y}
some_flag: {flag}
where num
, x
, y
, and flag
are placeholders that will be replaced by values when making copies of the template directory.
To make copies of this template directory we can use the dir-wand
CLI tool. dir-wand
needs a set of values for each placeholder along with the path to the template directory, e.g.:
dir-wand --template simple_example_{num}/ --root /where/to/put/copies/ --num 0-2 --x 1-3 --y 2-4 -flag 0 1 0
Here we've passed the filepath to the template directory (which can be an absolute or relative path), an optional root directory to contain the copies (if not given the copies will be made in the current working directory) and a set of key-value pairs for each placeholder (of the form --key value). These values can be:
- The definition of an inclusive range using 2 dashes (e.g.
--num 0-2
will replacenum
with values of 0, 1, and 2). - A list of values using 1 dash (e.g.
-flag 0 1 0
will replaceflag
with 0, 1, and 0). - The path to a file containing a list of strings using 2 dashes (for details see below).
NOTE: The number of values for each placeholder must be the same. If not, WAND will raise an error.
This will create 3 directories in /where/to/put/copies/
:
├── simple_example_0/
│ ├── simple_example_0.yaml
│ ├── nested_dir/
│ │ ├── some_text.txt
│ │ └── another_nested_dir/
│ │ └── another_another_dir/
│ │ └── nested_file.txt
│ └── empty_dir/
├── simple_example_1/
│ ├── simple_example_1.yaml
│ ├── nested_dir/
│ │ ├── some_text.txt
│ │ └── another_nested_dir/
│ │ └── another_another_dir/
│ │ └── nested_file.txt
│ └── empty_dir/
└── simple_example_2/
├── simple_example_2.yaml
├── nested_dir/
│ ├── some_text.txt
│ └── another_nested_dir/
│ └── another_another_dir/
│ └── nested_file.txt
└── empty_dir/
and simple_example_0.yaml
will contain:
PlaceHolders:
run_number: 0
run_directory: simple_example_0
variable: 1
another_variable: 2
some_flag: 0
with the other files made accordingly.
Rather than explicitly stating the values for a placeholder, you can pass the path to a file containing a list of values. For example, if we have a file values.txt
containing values split by newlines,
0
1
0
we could instead pass this file to the flag argument,
dir-wand --template simple_example_{num}/ --root /where/to/put/copies/ --num 0-2 --x 1-3 --y 2-4 --flag values.txt
which will create the same directories as before.
If you have a large number of placeholders or a large number of values for placeholders, it could be cumbersome to pass them all as arguments. Instead, you can pass a "swapfile", a yaml file defining the values to swap with each placeholder. If we have a swapfile swapfile.yaml
containing:
num:
range: 0-2
x:
list:
- 1
- 2
- 3
y:
range: 2-4
flag:
file: values.txt
we could instead pass this file to the --swapfile
argument,
dir-wand --template simple_example_{num}/ --root /where/to/put/copies/ --swapfile swapfile.yaml
ignoring the need to pass the placeholders as arguments explictly to get the same result as the calls detailed above. This not only makes working with a large number of placeholders easier but also allows for easy reuse of the same values across different runs.
When making copies of a template directory, WAND can also run commands in each directory. This can be done by passing the --run
argument followed by the command to run wrapped by "
. For example:
dir-wand --template simple_example_{num}/ --root /where/to/put/copies/ --num 0-2 --x 1-3 --y 2-4 -flag 0 1 0 --run "echo 'Hello from simple_example_{num}'"
will create the directories as before and run the command echo 'Hello from simple_example_{num}'
in each directory. This will output:
Hello from simple_example_0
Hello from simple_example_1
Hello from simple_example_2
These commands can be any valid shell command or even a script. The commands will be run on concurrent threads to ensure the main thread making the copies is not blocked so the world is your oyster!
WAND can also be used to run commands in existing directories with identical structures and "placeholder compliant" naming.
This can be done by passing the --run
argument including placeholders and any required swaps (which can be defined in any of the ways detailed above including a swapfile). For example, if we assume the example template directory above is already made, where we have the directories (including all their contents):
├── simple_example_0/
├── simple_example_1/
└── simple_example_2/
Then we can run a command in each directory using:
dir-wand --run "cd simple_example_{num}; head -2 simple_example_{num}.yaml" --num 0-2
This will output the first 2 lines of each simple_example_{num}.yaml
file in each directory. Note that any command passed to --run
will be run in the current working directory.
Invoking dir-wand
with the --help
flag (or not arguments at all) will display all possible options:
usage: dir-wand [-h] [--template TEMPLATE] [--root ROOT] [--run RUN] [--swapfile SWAPFILE] [--silent] [-- VALUE] [- VALUE [VALUE ...]]
Wave your Directory WAND and make magic happen.
options:
-h, --help show this help message and exit
--template TEMPLATE The template to use for the model.
--root ROOT The root directory for the outputs.
--run RUN A command to run in each copy once the copy is complete. The command will be run in the current working directory.
--swapfile SWAPFILE A yaml file defining the swaps for each placeholder.
--silent Suppress all WAND prints.
-- VALUE A key-value pair for a placeholder replacement. Should be in the form --key value, where key is the name ({name}) to replace in directory paths and files, and value is an inclusive range (1-5), or a filepath to a file contain a list.of values.
- VALUE [VALUE ...] A key-value pair for a placeholder replacement. Should be in the form -key value1 value2 value3, where key is the name ({name}) to replace in directory paths and files, and value is a list.