patternbashMinor
Running a script recursively in subdirectories
Viewed 0 times
scriptsubdirectoriesrunningrecursively
Problem
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
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:
Usage example:
Will output something like:
The reason
```
fatal: Not a git repository (or any of the parent direc
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_DIRUsage example:
recurse.sh "git status 2>&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 cleanThe reason
2>&1 is there is because otherwise this, which goes to stderr would be printed:```
fatal: Not a git repository (or any of the parent direc
Solution
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:
Avoid cd
If that's ok, then everything's fine.
If not, then you might want to add some input validation, for example:
Suggested implementation
Putting it together (coming to you in a Pull Request very soon):
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
fiSuggested 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"Code Snippets
(
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
)if [ ! "$RUN_SCRIPT" ]; then
echo usage: $0 'some script'
exit 1
fi#!/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"Context
StackExchange Code Review Q#112154, answer score: 8
Revisions (0)
No revisions yet.