I have a script that will iterate a set of git checkouts and run a git command in each directory.

I now want to be able to pass a parameter to the shell script from the command line and have that parameter incorporated into the git command being run.

I am using the following script as a test (I've removed the git commands until I can see the parameter being displayed).

#!/usr/bin/env bash
find . -type d -exec test -e '{}/.git' ';' -print -prune | xargs -P 1 -I {} sh -c 'cd {}; echo `pwd` $1; echo ""'

If I run the command with a parameter, I'm expecting to see the name of the directory that contains a .git directory and the parameter.

I only get the directory name.

After a LOT of playing around, I ended up with this ...

#!/usr/bin/env bash
find . -type d -exec test -e '{}/.git' ';' -print -prune | BRANCH="$1" xargs -P 1 -I {} sh -c 'cd {}; echo `pwd` "${BRANCH}"; echo ""'

Is there a mechanism I have missed? Is there a "better" way to pass on the arguments?

  • @Jeff: OP clearly has tag bash in the interpreter. I don’t see why it shouldn’t be tagged bash
    – Inian
    May 24 '19 at 11:30
  • @Inian None of the code shown depends on bash.
    – Kusalananda
    May 24 '19 at 13:09
  • 1
    With the she-bang interpreter set to bash, I assumed OP was targeting scripts for bash
    – Inian
    May 24 '19 at 13:35

From within your shell script you invoke another shell with an in-line script. Since you put this script between single quotes, the $1 is passed as is. The new shell you just launched, has its own set of parameters, which happen to be empty. So when parsing $1 it expands to nothing.

In your working example you assign your value to a new variable that is visible in the new shell.

If you're just trying to find all git repositories, you may also copy my all-git script below. Usage is simple: all-git <git command>, e.g. all-git status -bs


for repo in `find $PWD -type d -name .git`
        cd $repo
        echo "${repo%/}: " | sed -e "s#$HOME#~#"
        git "$@"
  • Thanks for this approach. This does seem to be working a lot easier for me that my attempt. May 24 '19 at 17:02
find . -type d -exec test -e '{}/.git' ';' -print -prune | \
    xargs -P $(nproc) -I {} sh -c "cd \"{}\"; echo \"\$(pwd) $1\"; echo "

Should work, but also note that you can call an exported function.

function doGitWork {
    cd "$DIR"
    echo "$(pwd) $OPT"
export -f doGitWork
find . -type d -exec test -e '{}/.git' ';' -print -prune | \
    xargs -P $(nproc) -I {} sh -c "doGitWork \"{}\";"
  • 1
    Good solution, but don't ever inject {} into the script that is called from find or xargs. Instead: sh -c 'cd "$1" && printf "%s %s\n" "$(pwd)" "$2"' sh {} "$1" (or better yet, call it with -exec directly from find to support all types of filenames) See also Is it possible to use `find -exec sh -c` safely?
    – Kusalananda
    May 24 '19 at 13:05

Using GNU Parallel it looks like this:

find . -type d -exec test -e '{}/.git' ';' -print -prune |
    parallel 'cd {}; echo $(pwd) {}; echo'

If you want to do git pull in all dirs that have a .git dir:

allgit() {
  find . -type d -name .git | 
      parallel --tagstring {//} "cd {//}; git $@"
allgit pull

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.