Reputation: 2173
I just don't get it, why I cannot do the thing I just mentioned in the question?
For more clarification, I am trying to loop through all the directories in a certain path, which is stored in any random variable of my choice. Please look at the below script and let me know why the execution doesn't get past the for
loop beginning statement when I run the script:
gpll_allrepos3() {
export SIMPROJS="/c/Projects/mainproject"
currPath=$(pwd)
for repoFolder in $SIMPROJS; do
echo "RepoFolder: $repoFolder"
if [[ -d "$SIMPROJS/$repoFolder/.git" && ! -L "$SIMPROJS/$repoFolder/.git" ]]; then
echo "$SIMPROJS/$repoFolder"
cd "$SIMPROJS/$repoFolder" && echo "Now inside $SIMPROJS/$repoFolder"
curr_branch=$(git rev-parse --abbrev-ref HEAD)
## git pull && git pull origin
## if [[ "$curr_branch" != "master" ]]; then
### Merging master branch as the current branch is feature branch
git checkout master
git pull && git pull origin
git checkout $curr_branch
git merge master
## fi
fi
done
cd $currPath
}
gpll_allrepos3
I am not a very good Bash scripter, so any suggestion is appreciated, but please note, I need to be able to run this script from anywhere I choose.
Upvotes: 0
Views: 356
Reputation: 4132
A script seems like overkill for the problem, can use find
: find $SOMEPATH -type d -name .git -execdir git checkout master \; -execdir git pull && git pull origin \; -execdir ... \;
.
find
directories (-type d
) named ".git" (-name .git
) in $SOMEPATH
. For each .git directory run command
from the subdirectory containing the matched file (-execdir command \; -execdir command \; -execdir ... \;
).
You don't need to cd
to the .git directory, you don't need to maintain a stack of directories (e.g. pushd
and popd
), etc. One-liner. Note: by default find
never follows symbolic links (ref. op's ! -L
).
Upvotes: 1
Reputation: 125778
for
doesn't iterate over the contents of directories, it iterates over a series of "words" (basically, strings). Thus, this statement:
for var in alice bob "jimmy joe" /c/Projects/mainproject; do
...will run its contents four times, first with var
set to "alice", then "bob", then "jimmy joe", then "/c/Projects/mainproject". It doesn't treat any of those as file or directory names, because you didn't ask it to. On the other hand, the shell will expand /c/Projects/mainproject/*
to a list of files in the directory /c/Projects/mainproject/ (including the path prefix). Thus, you could use:
for repoFolder in "$SIMPROJS"/*; do
The wildcard string will expand to a series of words (corresponding to the files and directories under /c/Projects/mainproject/), and then for
will iterate over those.
Note the double-quotes around the variable reference? It's almost always a good idea to do this, otherwise the shell will split the variable's contents into separate words based on spaces (or whatever's in the IFS
variable), and expand wildcards in the variable's value, and you usually don't want that (and if it happens unexpectedly, it can cause trouble). On the other hand, the *
has to be outside the double-quotes so it'll expand to a list of files, not just be treated as a literal filename.
Also, note that this will iterate over files as well as subdirectories in /c/Projects/mainproject. If you want just subdirectories, use this:
for repoFolder in "$SIMPROJS"/*/; do
The added trailing slash will restrict it to just matching directories (because a slash at the end of a directory's path makes sense, but at the end of a plain file's path it doesn't). But note that the trailing slash will get included in the resulting paths as well.
Other notes: don't use "$SIMPROJS/$repoFolder"
etc -- the repoFolder
variable already includes the path prefix (because it was included in the wildcard string that got expanded), so adding it again will cause trouble. Just use "$repoFolder"
.
Also, when you use cd
in a script, you should always include error handling in case it fails for some reason. Otherwise, the script will continue running in the wrong place, probably leading to chaos. You use:
cd "$SIMPROJS/$repoFolder" && echo "Now inside $SIMPROJS/$repoFolder"
...if this fails (which it will because of the path-doubling problem), it will not print the "Now inside..." message, but will then continue on with everything else. What you should use is something more like this:
cd "$repoFolder" || {
echo "Couldn't cd into $repoFolder, cancelling..." >&2
return 1
}
...which will exit the function rather than blindly continuing in the wrong place.
BTW, shellcheck.net is good at spotting common problems like these; I recommend running your scripts through it, at least until you get more familiar with shell scripting.
Upvotes: 1
Reputation: 42698
To loop through files in a directory you need to provide a string that can be expanded to a filename glob.
Once you have that, your loop variable will contain the entire path, so no need to prefix the original directory name.
Also you can use pushd
and popd
to avoid creating your $currPath
variable.
#!/bin/bash
simprojs="/c/Projects/mainproject"
for repoFolder in "$simprojs"/*; do
if [[ -d "$repoFolder/.git" && ! -L "$repoFolder/.git" ]]; then
pushd "$repoFolder" && echo "Now inside $repoFolder"
curr_branch=$(git rev-parse --abbrev-ref HEAD)
git checkout master
git pull && git pull origin
git checkout "$curr_branch"
git merge master
fi
done
popd
Upvotes: 1