Running a script recursively in subdirectories I got tired of running the same command in multiple directories, so I thought "there has to be a way to make this easier". The commands I was running was mostly git status, git stash list, hg summary, mvn clean test, and so on and so on...

So what I did was to create a bash script that you can pass a parameter to. It will then run the same script or command in all subdirectories, until it finds a place where the script returns exit status 0 (meaning that it was run successfully), then it stops going deeper in those subdirectories.

As I am not that used to bash-scripting, I am wondering if there's anything I can improve here. I would also like you to comment on the usability of this script. You are also welcome to make feature-requests, bug reports, or pull requests (my favorite!) on my bash-recursive repository on GitHub.

The script is:

#!/bin/bash

RUN_SCRIPT=$1

recurseCheck() {
  local f
  for f in $1/* ; do
    local PREV_DIR=`pwd`
    if [ -d $f ]; then
      cd $f
      RESULT=`eval $RUN_SCRIPT`
      local RESULT_CODE=$?
      if [ $RESULT_CODE -eq 0 ]; then
        echo "$f"
        echo "$RESULT"
        echo ""
      else
        recurseCheck $f
      fi
      cd $PREV_DIR
    fi
  done;
}

START_DIR=`pwd`
recurseCheck $START_DIR


Usage example:


  recurse.sh "git status 2&gt;&amp;1"


Will output something like:


/home/zomis/gitstuff/a/Duga
On branch master
nothing to commit, working directory clean

/home/zomis/gitstuff/a/SudokuSharp
On branch master
nothing to commit, working directory clean

/home/zomis/gitstuff/c/Brainduck
On branch master
nothing to commit, working directory clean

/home/zomis/gitstuff/c/CodeReview-Shield
On branch master
nothing to commit, working directory clean

/home/zomis/gitstuff/c/SE-Scripts
On branch master
nothing to commit, working directory clean



The reason 2&gt;&amp;1 is there is because otherwise this, which goes to stderr would be printed:

fatal: Not a git repository (or any of the parent directories): .git


There is also an "example" directory on my GitHub repo which contains a script that I used a lot while testing, run ./recurse.sh /path/to/bash-recurse/example/filecheck.sh and it will output something like:


/home/zomis/bash-recurse/example/b/d
Running filecheck in /home/zomis/recursescript/example/b/d
Hello World
This is a recursive script

/home/zomis/bash-recurse/example/c
Running filecheck in /home/zomis/recursescript/example/c
OK in directory 'c'



Any comments welcome.
Check yourself before you wreck yourself...

shellcheck.net is an awesome website that checks your Bash syntax.
Among other things, it will tell you to:


Double-quote your path variables
Use $(...) instead of the legacy `...` for sub-shells


Avoid cd inside scripts

cd inside scripts is troublesome.
In case something goes wrong,
you might find yourself in unexpected places, and wreak havoc.

But if you must, like in your example,
there's a better way than saving the previous directory in PREV_DIR and then cd "$PREV_DIR" to go back, using a (...) sub-shell environment:

(
cd "$f" || break
RESULT=$(eval "$RUN_SCRIPT")
local RESULT_CODE=$?
if [ $RESULT_CODE -eq 0 ]; then
    echo "$f"
    echo "$RESULT"
    echo
else
    recurseCheck "$f"
fi
)


Notice that there is only one cd in, no cd out.
This is because the cwd changes only affect the environment inside the (...).

Also notice the cd "$f" || break: if the cd command fails for some reason,
you probably don't want to execute the rest of the block.
You might go as far as exit at that point (shellcheck recommends it too) and investigate the troublesome directory.

Minor things

You had a pointless ; in done;, and instead of echo "" you can write simply echo.

Input validation

What if you run this script without arguments?
eval "" exits with success, so it will simply print all directories.
If that's ok, then everything's fine.
If not, then you might want to add some input validation, for example:

if [ ! "$RUN_SCRIPT" ]; then
    echo usage: $0 'some script'
    exit 1
fi


Suggested implementation

Putting it together (coming to you in a Pull Request very soon):

#!/bin/bash

RUN_SCRIPT=$1

recurseCheck() {
    local f
    for f in $1/* ; do
        if [ -d "$f" ]; then
            (
            cd "$f" || break
            RESULT=$(eval "$RUN_SCRIPT")
            local RESULT_CODE=$?
            if [ $RESULT_CODE -eq 0 ]; then
                echo "$f"
                echo "$RESULT"
                echo
            else
                recurseCheck "$f"
            fi
            )
        fi
    done
}

START_DIR=$(pwd)
recurseCheck "$START_DIR"