1

I'm attempting to find the "root" of a folder. I'm doing this in a Bash script with the following (at least in my head):

# Get current directory (e.g. /foo/bar/my/subdir)
CURR_DIR = `cwd`
# Break down into array of folder names
DIR_ARRAY=(${CURR_DIR//\// })

# Iterate over items in DIR_ARRAY starting with "subdir"
<HELP WITH FOR LOOP SYNTAX>
  # Each loop:
  # build path to current item in DIR_ITER; e.g.
  #   iter N: DIR_ITER=/foo/bar/my/subdir
  #   iter N-1: DIR_ITER=/foo/bar/my
  #   iter N-2: DIR_ITER=/foo/bar
  #   iter 0: DIR_ITER=/foo
  # In each loop:
  # get the contents of directory using "ls -a"
  # look for .git
  # set ROOT=DIR_ITER

export ROOT

I've Googled for looping in Bash but it all uses the "for i in ARRAY" form, which doesn't guarantee reverse iteration order. What's the recommended way to achieve what I want to do?

jkang
  • 483
  • 7
  • 19
  • 4
    `CURR_DIR = `cwd`` is invalid - it would execute a command named `CURR_DIR`. `# look for .git` so do you want to find `.git` or list contents of a directory? Why not do it [like here Is there a way to get the git root directory in one command?](https://stackoverflow.com/questions/957928/is-there-a-way-to-get-the-git-root-directory-in-one-command)? Is this XY question? Are you really asking how to find `.git` root dir? Is asking about iterating an array from the end an XY problem? – KamilCuk Aug 13 '20 at 19:29
  • 2
    There are no spaces around `=` in shell assignments. – Barmar Aug 13 '20 at 19:30
  • You can't iterate in reverse order with `for i in ...`. You need to iterate over the array indexes numerically. – Barmar Aug 13 '20 at 19:31
  • Thanks for the feedback. My current specific problem is finding the .git root. However I'd also like to learn how to do this type of iteration with Bash scripts in case I want to apply this to other searching methods. – jkang Aug 13 '20 at 19:44

2 Answers2

1

One idea on reverse index referencing.

First our data:

$ CURR_DIR=/a/b/c/d/e/f
$ DIR_ARRAY=( ${CURR_DIR//\// } )
$ typeset -p DIR_ARRAY
declare -a DIR_ARRAY=([0]="a" [1]="b" [2]="c" [3]="d" [4]="e" [5]="f")

Our list of indices:

$ echo "${!DIR_ARRAY[@]}"
0 1 2 3 4 5

Our list of indices in reverse:

$ echo "${!DIR_ARRAY[@]}" | rev
5 4 3 2 1 0

Looping through our reverse list of indices:

$ for i in $(echo "${!DIR_ARRAY[@]}" | rev)
do
    echo $i
done
5
4
3
2
1
0

As for working your way up the directory structure using this 'reverse' index strategy:

$ LOOP_DIR="${CURR_DIR}"
$ for i in $(echo "${!DIR_ARRAY[@]}" | rev)
do
    echo "${DIR_ARRAY[${i}]}:${LOOP_DIR}"
    LOOP_DIR="${LOOP_DIR%/*}"
done
f:/a/b/c/d/e/f
e:/a/b/c/d/e
d:/a/b/c/d
c:/a/b/c
b:/a/b
a:/a

Though we could accomplish the same thing a) without the array and b) using some basic parameter expansions, eg:

$ LOOP_DIR="${CURR_DIR}"
$ while [ "${LOOP_DIR}" != '' ]
do
    subdir="${LOOP_DIR##*/}"
    echo "${subdir}:${LOOP_DIR}"
    LOOP_DIR="${LOOP_DIR%/*}"
done
f:/a/b/c/d/e/f
e:/a/b/c/d/e
d:/a/b/c/d
c:/a/b/c
b:/a/b
a:/a
markp-fuso
  • 28,790
  • 4
  • 16
  • 36
0

You can use dirname in a loop, to find the parent folder, then move up until you e.g., find the .git folder.

Quick example:

#!/usr/bin/env bash
set -eu

for arg in "$@"
do
    current=$arg
    while true
    do
        if [ -d "$current/.git" ]
        then
            echo "$arg: .git in $current"
            break
        fi
        parent="$(dirname "$current")"
        if [ "$parent" == "$current" ]
        then
            echo "No .git in $arg"
            break
        fi
        current=$parent
    done
done

For each parameter you pass to this script, it will print where it found the .git folder up the directory tree, or print an error if it didn't find it.

Robert
  • 7,394
  • 40
  • 45
  • 64