2

I'm quite new to bash scripting so excuse the rough script. I'm writing a simple script that goes through all directories that has a .git directory in them, and checks if the worktree is clean or dirty. It works, but I want to have 3 parameters:

  • rootDir: top level directory to search for .git directories.
  • status: clean|dirty|all which will filter the output based on the status of the direcotries
  • separator: Allows passing null which would do a \0 separator so that I can pass this output to xargs and further run commands while supporting paths with spaces.

The problem I'm facing is with passing parameters from the fd exec to my isRepoDirty function. The $cleanOrDirty parameter somehow makes it in, but the $separator parameter doesn't work. My single quote, double quote knowledge is a bit shaky here... If I have my params inside the single quotes, they never get expanded.

Here is what I have:

#!/bin/bash
# DOES NOT WORK IN ZSH!
# https://unix.stackexchange.com/questions/50692/executing-user-defined-function-in-a-find-exec-call

rootDir=$1 cleanOrDirty=$2 separator=$3

echo "Root: $rootDir"

echo "Clean or Dirty: $cleanOrDirty"

echo "Separator: $separator"

function isRepoDirty() { if [[ $(git --git-dir "$1" --work-tree "$1/.." status --porcelain | wc -l) > 0 ]]; then status="dirty" else status="clean" fi

# echo "Repo: $1, Status: $status"
if [[ $2 == $status || $2 == "all" ]]; then
    # echo "Separator: $3"
    if [[ $3 == "null" ]]; then
        printf "%b" "$1/..\0"
    else
        echo "$1/.."
    fi
fi

} export -f isRepoDirty

fd --no-ignore --hidden -t d ".git$" $rootDir -x /bin/bash -c 'isRepoDirty "{}" '$cleanOrDirty $separator

If my approach to this is totally off, please do tell me as I'm quit keen to learn and do what's considered best practise. Heh my passing of args is a bit rudimentary. I haven't found a simple way to easily handle -p and --parameter-name style arguments easily in bash.

Thanks for your time!

Albert
  • 141

1 Answers1

0

I had a similar issue and fixed it by adding a bash argument, like so:

bash -c 'command "$@"' bash arg1 arg2

as Stéphane Chazelas noted, the first argument is treated as $0 to the called command, so this will make debugging easier than _, which is what I originally had.

In your case, I think the answer would look like so:

fd --no-ignore --hidden -t d '\.git$' "$rootDir" -x /bin/bash -c 'isRepoDirty "$1" "$2" "$3"' bash {} "$cleanOrDirty" "$separator"

Note that it assumes that $rootDir doesn't start with - and that none of $cleanOrDirty and $separator is ; or contains {}, {/}, {//}, {.}, {/.}.

To avoid those, you could use the -- option delimiter, meaning moving -x before the pattern, and pass the contents of those extra variables via the environment rather than arguments:

(
  export cleanOrDirty separator
  exec fd --no-ignore --hidden -t d -x /bin/bash -c '
    isRepoDirty "$1" "$cleanOrDirty" "$separator"
    ' bash {} ';' -- '\.git$' "$rootDir"
)