My Command Engine or MyCE (as in "Mice") is a powerful, context-aware command-line tool, written intirely in bash, and designed to streamline project workflows by utilizing custom command definitions stored in .myCommands files within the directory tree.
It searches for .myCommands files from the root down to the current directory, merging commands to create a localized and context-sensitive command set.
The ~/.myCommands file will always be processed first, even if not executed within your $HOME directory.
This tool is ideal for developers who frequently switch between projects or environments and need specific commands scoped to each context.
[server]
start="docker-compose up"
stop="docker-compose down"Running my server.start will execute docker-compose up.
-
Context-Aware Commands: Executes commands based on the
.myCommandsfile located in the current directory or its parent directories. - Scoped Command Aliases: Each directory (or project) can define its own command aliases without impacting other directories.
-
Ease of Use: A simple command
my <key>is all you need to invoke the corresponding full command defined in the.myCommandsfile. -
Recursive Lookup: If a
.myCommandsfile is not found in the current directory, the script searches parent directories until one is found. -
Merged Configurations: Reads
.myCommandsfiles from the root directory down to the present working directory, merging them to build a complete command set. If duplicates are found, the command closest to the current directory takes precedence. -
Sectioned Commands: Uses INI-style sections in
.myCommandsfiles to organize and access commands with dot-delimited syntax. This allows grouping related commands for better clarity and organization: -
Positional Args: Values within the
.myCommandsfile can use positional argument references (ie $1,$2, $ {3}) -
Default Variable Values: Default values for variables may be set using the following syntax:
${1:-defaultValue}or${CONST:-defaultValue} - Argument Passing: Additional arguments provided after the command alias are passed directly to the underlying command. This enables dynamic behavior and flexible command usage.
-
Fallback to Shell: If the requested alias is not found in the merged
.myCommandsconfigurations, the script will pass the command to the shell, allowing standard shell commands to work seamlessly withmy. -
Cross-Domain Commands with Variables: Commands can reference variables set in different
.myCommandsfiles, allowing for reusable, high-level command configurations across directories. This feature is useful for defining generic commands in higher-level folders and reusing them in specific contexts within the workspace. - Include Files: Other files can be included for re-use and/or keeping secret credentials in separate files
The .myCommands files uses INI-style sections to allow optional grouping, and key-value pairs where each key is an alias for a command, and the value is the corresponding command.
Other files may be included by using the include <FILE> syntax.
The path may be absolute or relative and should follow the same INI-style syntax.
If you want to include .myCommands in source control, it is recommended that you take advantage of this feature to store your credentials in a separate file.
Example .myCommands file:
# Include other files
include credentials.secret
# Variables
LAST_CONTAINER="podman exec -it"
DB_CONTAINER="pod-db"
# Commands
echo=echo "Custom Prefix: "
npm="$LAST_CONTAINER npm"
[db]
backup=podman exec --interactive --tty --rm $DB_CONTAINER mongodump
restore=podman exec --interactive --tty --rm $DB_CONTAINER mongorestoreCreate a .myCommands file in the root of your project or any directory where you want to define custom commands.
Run the my script followed by the alias you want to execute, and any additional arguments you may want to pass.
For example, if you're in a directory with the previous .myCommands file:
# Usage:
# my <alias>
~>$ my echo Hello World
Custom Prefix: Hello World
~>$ my db.backup --gzip
# Runs: podman exec --interactive --tty --rm pod-db mongodump --gzipThe my script has the following internal commands.
These commands take priority over any keys or aliases of the same name that are defined in any .myCommands files, so consider them reserved keywords.
Don't worry if you forget the basics.
Simply type my help to view the usage details.
$> my help
This script will search the current directory tree for a file named '.myCommands', containing
a key that matches the first argument. If found, the value of that key will be used instead.
This can be used to allow local overriding of certain commands to point to a container
instead of the local installed command.
USAGE:
my [OPTIONS] [help | version | set | list [-l] | KEY [args...]]
<KEY> [...args] The key of the command to run
list [-l] [-a] List the available command keys.
Include the '-l' arg to list one key per line. Default is column view.
Include the '-a' arg to include variables (uppercase)
definition <key> Identifies the file(s) the given key exist in.
set <key> <command> Adds or updates the 'key' to the '.myCommands' file in the current directory.
The given command will be used whenever called from this level or a descendant.
update [diff] Update the script with the latest version. If 'diff' is given, then show the changes
help Show usage
version Show version number
OPTIONS:
-c Run the command without sourcing the default shell rc file
-v Verbose Level (Multiple may be given to increase the verbosity)
-d Dry Run.
By: Jerren Saunders
Version: 26.2.14Not sure if you have the latest version installed?
Run my version and compare against the latest version on GitHub.
Caution
Experimental!
This command may be used to add a new value to the .myCommands file in the current directory.
The first argument is the key/alias, all remaining arguments will be treated as the value.
If the command should belong inside an INI section, then use a '.' to separate group from the key.
For example:
my set group.key echo "Hello World"Will be stored as:
[group]
key=echo "Hello World"Can't remember what you used as the key?
Just enter my list to view the available commands.
If you don't like columns, add the -l option at the end to show one command per line.
If you want to view all definitions (including variables), add the -a option.
>$ my list
The following commands are available:
projA.env.backup pod.log swap.on
projA.env.diff pod.ls swap.status
projA.env.view pod.replace test.dblpos1
projB.env.backup pod.replace+ test.dblpos2
projB.env.diff pod.run test.dblpos3It can be easy to forget which .myCommands file you added a definition especially if you're attempting to edit it.
To help locate which file(s) a certain command is defined, you can use the definition action to show it's references.
The output will contain the full file path, line number, command reference (section and command), and the value (no substitutions) given in that file.
~/code/MyCE/tests/projectB>$ my definition pod.ls
/home/jerren/.myCommand:91 [pod]⇒ ls podman ps --all --format "table {{.Image}} {{.RunningFor}} {{.Status}} {{.Names}} "
/home/jerren/code/MyCE/.myCommands:94 [pod]⇒ ls podman ps --all --format "table {{.Image}} {{.RunningFor}} {{.Status}} {{.Names}} "
/home/jerren/code/MyCE/tests/.myCommands:7 [pod]⇒ ls ${pod.CONTAINER_EXE} ps --all --format "table {{.Image}} {{.RunningFor}} {{.Status}} {{.Names}} "Easily pull down updates from the github repo.
The file will automatically update the MyCE script (tpyically /usr/local/bin) and the permissions set to 755.
This command needs to be run with root level privileges.
If not, it will attempt to elevate itself and prompt for a password if needed.
If you only want to view the changes (uses sdiff) between your local version and the latest version on GitHub, you can use my update diff.
You can also reference a version tag with the diff command.
If not specified, the latest released version will be assumed.
By default, the latest released version will be installed.
You can explicitly reference this with either my If you want to grab the latest version from the mainbranch, specify eitherheadormain`.
You may roll-back to a previously release version by specifying the tag. To view all available tags, see GitHub Repo Tags.
my update diff
my update diff head
my update diff v26.2.13
my update head
my update v26.2.13Important
Options for the my script should be added immediately following the my script call (before the key).
Log prints can be enabled for debugging by including the -v option.
Crank up the level by stacking more (-vv, -vvv).
Three levels is currently the most verbosity used in log prints, but if you get a little trigger happy with the v key, it will be ok 😃.
my -v build
my -vv build
my -vvv buildCurious how your .myCommands entries will expand, but not brave enough to just try?
A dry run can be enabled by using the -d option.
Tip
In dry run mode, the expanded command will be printed, but not executed
$> my -d pod.con
---- THIS IS A DRYRUN! ----
CMD: podman exec -it my-container bash Include this option if you don't want your shell rc file to expand for this execution. To always disable sourcing the shell rc file, see MYCE_RUNCOM.
my -c buildIf you're reading this section, welcome fellow nerd 🤓!
-
Merging and Overriding: The script starts with
~/.myCommandsfile if it exists, then moves to the root directory and works down to the current directory (pwd), merging any.myCommandsfiles found along the way. Any collisions with duplicate keys names will overwrite previous definition values. This means that commands defined in.myCommandsfiles closer to the current directory override duplicates from higher-level directories. This makes the command engine adaptable for different projects, but also allows for globally defined aliases. It is important to note that the definitions are not processed during this step, so no variable substitutions will be performed. -
Sectioned Access: Commands are referenced using
section.keysyntax if using INI-style sections. -
Fallback to Shell: If no matching alias is found, the command is sent to the shell for evaluation.
Variables can be set in .myCommands files at any directory level and accessed by commands in lower directories, allowing for flexible and reusable configurations.
To define a variable, use only uppercase letters, underscores, and dashes (ie /[A-Z_-]/).
Variables may be defined inside of a section if desired for better organization of your definitions.
These variables are excluded from the output the list subcommand by default unless the -a option is specified.
# ~/code/.myCommands
UBUNTU_IMAGE=ubuntu:22.04
[pod]
CONTAINER_EXE=podman
ls=${pod.CONTAINER_EXE} ps --all --format "table {{.Image}} {{.RunningFor}} {{.Status}} {{.Names}} "
dist=${pod.CONTAINER_EXE} run -it --rm ${UBUNTU_IMAGE} bash -c "cat /etc/lsb-release | grep DESCRIPTION"
terminal=${pod.CONTAINER_EXE} run -it --rm ${UBUNTU_IMAGE} bash# ~/code/project_B/.myCommand
UBUNTU_IMAGE=ubuntu:20.04
[pod]
CONTAINER_EXE=docker~/code/projectA>$ my pod.dist
# CMD: podman run -it --rm ubuntu:22.04 bash -c "grep DESCRIPTION /etc/lsb-release | cut -d'=' -f2"
"Ubuntu 22.04.5 LTS"
~/code/projectB>$ my pod.dist
# CMD: docker run -it --rm ubuntu:20.04 bash -c "grep DESCRIPTION /etc/lsb-release | cut -d'=' -f2"
"Ubuntu 20.04.6 LTS"
# Notice this used docker and Ubuntu 20.04 instead of podman and 22.04
~/code/projectC>$ my pod.dist
# CMD: podman run -it --rm ubuntu:22.04 bash -c "grep DESCRIPTION /etc/lsb-release | cut -d'=' -f2"
"Ubuntu 22.04.5 LTS"Other files may be included by adding a include <FILE> line in a .myCommands file.
The key-values defined in the referenced file will be processed as soon as the line is detected.
This means that any definitions in the included file will overwrite any previously defined values and will be overwritten by any values processed later.
This feature is beneficial if you want to keep credentials and secrets in a separate file that is excluded from repositories, but you want the .myCommands file to be part of the version history for sharing with other developers.
# File: ./.credentials.secret
[secret]
password1=$3cr3+
[toolA]
APIKey=0123456789ABCDEF# File: ./.myCommands
# Relative paths are supported
# include ./tests/../credentials.secret
# Paths with environment variables are supported
# include $HOME/credentials.secret
# Absolute paths are supported
include ./.credentials.secret
[secret]
localcred=echo 'Password from PWD: "${secret.password1}"'
[toolA]
health=echo curl -s -H "Authorization: Bearer ${toolA.APIKey}" https://api.example.com/status> my secret.localcred
Password from PWD: "$3cr3+"
> my toolA.health
curl -s -H Authorization: Bearer 0123456789ABCDEF https://api.example.com/status-
Download the File: Ensure the script file (
my) is placed in a directory that is included in your system's $PATH. It is recommended to install at/usr/local/bin/myThe permissions of the downloaded file will also need to be updated to allow for execution# Note: You may need to run with `sudo` curl -o /usr/local/sbin/my https://raw.githubusercontent.com/jerrens/MyCE/refs/heads/main/my chmod +x /usr/local/sbin/my -
Create
.myCommandsfiles: Add a.myCommandsfile in the root directories of your projects.A sample is available at https://github.com/jerrens/MyCE/blob/main/.myCommands.example
Optional: You can add my to your .bashrc or .zshrc if you prefer:
alias my='/path/to/my'Optional environment variables may be defined to override default values for MyCE.
These variables should be set for the current session by using export <VARIABLE>=<VALUE>
or permanently by defining in .bashrc or .zshrc.
By default, MyCE will load command definitions from .myCommands files in the current directory tree.
Set the value of this variable to the desired file name if you wish to override the file name used.
By default, the list action will use a column width of 120 when printing aliases.
Set the value of this variable to the desired width, or to FULL to use the full terminal width.
By default, MyCE will source the shell's rc file (eg. ~/.bashrc, ~/.zshrc, ~/.kshrc) to the subshell so that user customizations defined there can be used.
Set the value of this variable to an alternative path if the file you want sourced is different from the default shell path (eg. export MYCE_RUNCOM=~/.profile).
Set the value of this variable to false if you do not want to source any rc file
Related to the -c command line option
To debug, either view verbose help (my -vv help) or search the output of a verbose command execution for the variable name (my -vvv echo "Hello" | grep "MYCE_RUNCOM")
You have two repositories, Repo1 and Repo2, both with their own .myCommands files.
# Repo1's `.myCommands` file:
build="mvn clean package"
run="java -jar target/app.jar"# Repo2's `.myCommands` file:
build="npm run build"
run="npm start"When you are working in Repo1. To build and run the application:
my build # Executes 'mvn clean package'
my run # Executes 'java -jar target/app.jar'You switch to Repo2. Now, running the same my build command:
my build # Executes 'npm run build'
my run # Executes 'npm start'The command executed changes based on the directory you are in, as it reads the .myCommands file from that directory.
The bash terminal offers a simple command completion feature using the TAB key while entering commands.
To take advantage of this feature with MyCE, if you already have the folder /etc/bash_completion.d you can run my update to download the latest release and install the auto-completion script.
You may need to run exec bash after the update for your shell to load the new feature
If you're using a different shell or location for your completion scripts, the manual installation instructions are:
- Copy the bash-completion/my file from this repo to
/etc/bash_completion.d/my - Enable the execute flag (
chmod +x)
This can be done with the following commands (these will likely need to be run as sudo)
curl -o /etc/bash_completion.d/my https://raw.githubusercontent.com/jerrens/MyCE/refs/heads/main/bash-completion/my
chmod +x /etc/bash_completion.d/myIf you run exec bash after doing the above, your environment will be reloaded and you can check to see if the command completion is working by entering my hel<TAB>.
If working, it should auto complete to my help.
If it does not, you likely need to add one of the following to your ~/.bashrc file to load the completion script when a session starts.
source /etc/bash_completion.d/my
to load the specific file, or to load all files in the /etc/bash_completion.d folder, you can add the line:
for f in /etc/bash_completion.d/*; do source "/etc/bash_completion.d/$f"; done
After adding one of those lines to ~/.bashrc, save, then run exec bash and try again.
If you'd like to learn more about MyCE, an article can be found at https://medium.com/@jerren/introduction-my-command-engine-myce-42e15028364a that goes into details and provides some examples.
If you find a bug or have a suggestion on a feature to add to MyCE, please open a GitHub Issue, or better yet submit a PR for consideration.
The Discussions page has been enabled on the repo as well. This is a good area to gauge interest in a feature idea with the community and also to share the command keys you’ve come up with for your .myCommands files and help others kick-start their setup.
MIT License
