Skip to content

Instantly share code, notes, and snippets.

@jmiserez
Last active June 12, 2019 13:48
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jmiserez/7f85ef5946b08399219ae6c3b60f20f3 to your computer and use it in GitHub Desktop.
Save jmiserez/7f85ef5946b08399219ae6c3b60f20f3 to your computer and use it in GitHub Desktop.
Bash: "Execute for all subdirectories", execute commands in all subdirectorys, cache output and print sequentially with locking
# Add this to your .bashrc and source it (source ~/.bashrc)
# Example: forallsubdirs basedir commands
# - forallsubdirs . "ls | wc -l; git status"
# - forallsubdirs . "git fetch --all; git merge --ff-only"
# Note:
# - To include hidden directories (starting with a "."), set the dotglob option in Bash: shopt -s dotglob
# - Silencing job control (see comments) is only partially possible, in certain cases job control output will still be printed (see https://stackoverflow.com/a/38278291/202504).
forallsubdirs() {
if [ -d "$1" ]; then
dir="$1"
shift
# create a tmp file and immediately delete it, but keep a handle
lockfile=$(mktemp /tmp/bashrc-execfsd-flock.XXXXXX); exec 3>"$lockfile"; rm "$lockfile"
for i in "${dir%/}"/*; do
if [[ -d "$i" && ! -L "$i" ]]; then # directories, exclude symlinks.
echo "$i"
# note: do not add further brackets, this only works for max 1 subshell. flock() to force sequential output.
{(res=$(exec 2>&1; echo "$i:"; cd $i; eval "$@"); flock 3; printf "\n%s\n" "$res") & } #2>/dev/null #uncomment to silence job control
fi
done
wait #2>/dev/null #uncomment to silence job control
echo "Done."
else
echo "not a directory: $1"
return 1
fi
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment