Quickly find or run commands in many projects
As I have many projects I often do the same tasks in multiple projects (like git fetch
).
Doing something like this is tedious:
ls
cd first
git fetch
cd ..
cd second
git fetch
cd ..
…
And can be done with this tool in a much simpler way:
project-below --directory=.git git fetch
# Or with an alias:
alias gitBelow='project-below --directory=.git git'
gitBelow fetch
Finding projects of a certain programming language below the current directory gets fairly easy this way:
cd ~
project-below --file=Cargo.toml
Output:
git/hub/EdJoPaTo/public/mqttui
git/hub/EdJoPaTo/public/project-below
git/hub/EdJoPaTo/public/website-stalker
…
This tool is always used in the following way:
project-below [OPTIONS] [COMMAND]...
With the OPTIONS
the sub-folders are filtered.
Then in every matching folder the COMMAND
is executed.
Check --help
for the available filters.
For example lets run git status
in every sub-folder which contains a package.json
(probably some Node.js project):
project-below --file=package.json git status
This can be simplified with aliases like it is done in the examples. The first part (executable and options) always stays the same for this kind of query, only the command (or its arguments) changes. You can put the first part in an alias and use the alias then:
alias npmBelow='project-below --file=package.json'
npmBelow git status
Run git status
or git fetch
in all git projects below the current directory:
alias gitBelow='project-below --directory=.git git'
gitBelow status
gitBelow status --short --branch
gitBelow fetch
alias cargoBelow='project-below --file=Cargo.toml cargo'
cargoBelow check
cargoBelow clean
alias dockerBelow='project-below --file=Dockerfile docker'
dockerBelow build .
alias podmanBelow='project-below --file=Dockerfile podman'
podmanBelow build .
alias dotnetBelow='project-below --file="*.sln" dotnet'
dotnetBelow test
dotnetBelow build
dotnetBelow clean
alias npmBelow='project-below --file=package.json npm'
npmBelow test
alias npmBelow-clean='project-below --file=package.json --directory=node_modules rm -rf node_modules'
npmBelow-clean
alias makepkgBelow='project-below --file=PKGBUILD makepkg'
makepkgBelow -f
alias pioBelow='project-below --file=platformio.ini pio'
pioBelow test
alias pioBelow-clean='project-below --file=platformio.ini --directory=.pio rm -rf .pio'
pioBelow-clean
alias website-stalker-below='project-below --file=website-stalker.yaml website-stalker'
website-stalker-below run --all
Feel free to add your own example via Pull Request!
Please keep git
and cargo
as the first examples.
After that all examples are alphabetically sorted.
Test your setup first without running a command and use without command or add echo
instead.
When building a command including for example rm
it's wise to test it before running it.
-alias cargoBelow='project-below --file=Cargo.toml rm -rf target'
+alias cargoBelow='project-below --file=Cargo.toml'
-alias cargoBelow='project-below --file=Cargo.toml rm -rf target'
+alias cargoBelow='project-below --file=Cargo.toml echo rm -rf target'
You can create a smart cd
command relatively easy with project-below
and fzf
.
For example switching into one of the git repositories can be done like this:
alias cdg='cd "$(project-below --directory=.git | fzf)"'
cdg
When using the listed output of found directories the result in unsorted due to multi-threading and the way the OS returns folders.
Using sort
is neat here:
-alias cdg='cd "$(project-below --directory=.git | fzf)"'
+alias cdg='cd "$(project-below --directory=.git | sort | fzf)"'
Builds on a smaller machine with not as much computing power are annoying to run in the background.
nice
helps by allowing the OS to prioritize other processes instead. (Sound servers like pipewire
are usually the opposite of nice to ensure no stutters in sound)
You can include it in your alias and all the commands will run via nice
:
-alias cargoBelow='project-below --file=Cargo.toml cargo'
+alias cargoBelow='project-below --file=Cargo.toml nice cargo'
This way processes will still take all the resources, but when multiple processes want them, the nicer one won't get as many. This is really neat for background or longer tasks that aren't time-critical.
Tip: nice -n 19
is the nicest we can get. nice
defaults to 10, so using nice -n 19
in aliases results in being even nice to commands with just nice
.
Some tools use a pager.
For example git
uses less
for some commands.
project-below
sets the environment variable PAGER
to cat
in order to work around this.
If you have set GIT_PAGER
or another tool specific pager this will not help here.
For example include it into your alias:
-alias gitBelow=' project-below --directory=.git git'
+alias gitBelow='GIT_PAGER=cat project-below --directory=.git git'
For some use cases this problem can be solved with find
or fd
, but gets annoying for some package managers.
gitBelow
for example can be realized like this:
gitBelow() {
find . -name ".git" -type d -print -execdir git --no-pager $@ \;
}
gitBelow fetch
Sadly this approach lacks auto-completion of git
commands: gitBelow stat<Tab>
does not auto-complete to gitBelow status
.
But this is only an annoyance.
For commands like npm
it's not even simple to come up with a useful solution.
find
does not use ignore files and ends up with all the package.json
files in the node_modules
folder (which is often a lot).
fd
on the other hand uses ignore files but --exec
is always executed from the working directory from where the command was started.
git
for example has -C
to use another path, but tools like npm
do not.
This requires some workarounds like spawning a bash
with a cd
command at first.
It works but there won't be auto-completion of commands either.
Also, it creates a lot of bash / alias dark magic my future me wants to understand.
It is also way harder to adapt to new use cases or other dependency managers.
In turn, I created this tool which helps me to do exactly what I need in a simple way.
As this tool uses the same directory walker like fd
or rg
it's way faster than find
can ever be and uses smart features like ignore files, it skips hidden folders and so on.