1

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?

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

3 Answers 3

1

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

#!/bin/bash

for repo in `find $PWD -type d -name .git`
do
    repo=${repo%.git}
    ( 
        cd $repo
        echo "${repo%/}: " | sed -e "s#$HOME#~#"
        git "$@"
    )
done
1
  • Thanks for this approach. This does seem to be working a lot easier for me that my attempt. May 24, 2019 at 17:02
1
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.

OPT="$1"
function doGitWork {
    DIR="$1"
    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
  • 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, 2019 at 13:05
0

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

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .