Search code examples
bashposixrequire-once

require_once for bash, but how to for older bash and POSIX-shell?


Inspired by the Php require_once I figured how it could be implemented for modern Bash with associative arrays here:

a.sh

#!/usr/bin/env bash

declare -gAi __REQUIRES

require_once() {
  for p; do
    r=$(readlink --canonicalize-existing "$p")
    # shellcheck disable=SC1090 # Dynamic source
    [[ -r "$r" && 1 -ne "${__REQUIRES[$r]}" ]] && __REQUIRES[$r]=1 && . "$r"
  done
}

require_once ./b.sh
require_once ./b.sh

hello

b.sh

hello() {
  printf 'I am the hello function in b.sh.\n'
}

This works as intended:

  1. Get the real path of the source.
  2. Check it is readable, check it is not already loaded by looking-up the global associative array key.
  3. If so, register it in the global associative array and source it.

Now I am still wondering how to:

  1. Get the real path of the source in a more portable/standard way not depending on Linux Gnu Tools?
  2. Have it work with older Bash like 3.2
  3. Have it work with POSIX-shell grammar without array.

Solution

  • Get the real path of the source in a more portable/standard way not depending on Linux Gnu Tools?

    readlink -f is available on Busybox. Do readlink after checking that the path exists.

    Anyway, https://www.google.com/search?q=readlink+POSIX -> https://medium.com/mkdir-awesome/posix-alternatives-for-readlink-21a4bfe0455c , https://github.com/ko1nksm/readlinkf .

    Have it work with older Bash like 3.2

    Have it work with POSIX-shell grammar without array.

    POSIX does not like newlines in filenames anyway, so just store files as lines:

    __REQUIRES=""
    if ! printf "%s" "$__REQUIRES" | grep -Fq "$r"; then
       __REQUIRES="$__REQUIRES""$r
    "
       . "$r"
    fi
    

    Or maybe use case so that you do not fork:

    __REQUIRES="
    "
    case "$__REQUIRES" in
    *"
    $r
    "*) ;;
    *) 
        __REQUIRES="$__REQUIRES""$r
    "
        . "$r"
        ;;
    esac
    

    If you want to handle newlines in filenames, convert filename via xxd or od (both are available on Busybox, od is POSIX) and store hex representation of filenames as lines in the variable.