Search code examples
bashrecursiondependencies

Bash function to get all dependencies recursively


I've got a command that provide me the list of direct dependencies of some package. Here it is, for example:

# get-dep some-random-package
libunistring-1.0
libidn2-2.3.2
glibc-2.35-163
xz-5.2.7
gcc-11.3.0-lib
attr-2.5.1
acl-2.3.1
gmp-with-cxx-stage4-6.2.1
coreutils-9.1
findutils-4.9.0
bash-5.1-p16
pcre-8.45
gnugrep-3.7
zstd-1.5.2
zstd-1.5.2-bin
zlib-1.2.13
openssl-3.0.7
libxml2-2.10.3
bzip2-1.0.8
libarchive-3.6.1-lib
libarchive-3.6.1
gzip-1.12
some-random-package

I need to get the whole list of dependencies.

by using this get-dep command I am trying to write bash function that give me the whole list of dependencies recursivly. At first glance this function should recursivly pass through each package. I tried to do next:

#!/bin/bash -e

get_the_whole_list_of_dependencies() {
  local list=$(get-dep "${1}")
  local cnt=$(echo "${list}"| wc -l)
  if [ "${cnt}" == 1 ]; then
    echo "${list}"
  else
    for f in ${list}; do
      get_the_whole_list_of_dependencies "${f}"
    done
  fi
}

get_the_whole_list_of_dependencies some-random-package

However something goes wrong and the function is turned to be an endless loop.

Does anybody help me with that issue?


Solution

  • As discussed in comments your algorithm is inefficient because it processes the same package as many times as it is encountered. Another bash array could be used to keep track of the already seen packages and avoid this.

    Note also that your script prints only the leaves of your dependency tree while you state that you want the whole list of dependencies recursively.

    If you use a bash array to avoid processing twice the same package, and if your bash supports them, better use an associative array (local -A seen) as it makes very easy to check if a package has already been seen.

    Example:

    _get_the_whole_list_of_dependencies() {
      printf '%s\n' "$1"
      local -a list=($(get-dep "$1"))
      seen["$1"]=1
      local -i cnt=${#list[@]}
      if (( cnt > 1 )); then
        for f in "${list[@]}"; do
          if ! [[ -v seen["$f"] ]]; then
            _get_the_whole_list_of_dependencies "$f"
          fi
        done
      fi
    }
    
    get_the_whole_list_of_dependencies() {
      local -A seen=()
      _get_the_whole_list_of_dependencies "$1"
    }
    

    And then:

    $ get_the_whole_list_of_dependencies zlib
    zlib
    xz
    gettext
    gettext-runtime
    libiconv
    gperf
    gettext-tools-libs
    libtextstyle
    ncurses