= Bash Notes

== basename

Don't use `basename`, use:

[,bash]
----
file=${path##*/}
----

== dirname

Don't use `dirname`, use:

[,bash]
----
dir=${path%/*}
----

== shopt

If you set `shopt` in a function, reset to its default state with `trap`:

[,bash]
----
func() {
  trap "$(shopt -p globstar)" RETURN
  shopt -q -s globstar
}
----

== find, grep, print0, -0, -z

Don't use `find` in `for` loops, because filenames can contain spaces.
Try to use `globstar` and `nullglob` or null byte terminated strings.

Instead of:

[,bash]
----
func() {
    for file in $(find /usr/lib* -type f -name 'lib*.a' -print0 ); do
      echo $file
    done
}
----

use:

[,bash]
----
func() {
    trap "$(shopt -p nullglob globstar)" RETURN
    shopt -q -s nullglob globstar

    for file in /usr/lib*/**/lib*.a; do
      [[ -f $file ]] || continue
      echo "$file"
    done
}
----

Or collect the filenames in an array, if you need them more than once:

[,bash]
----
func() {
    trap "$(shopt -p globstar)" RETURN
    shopt -q -s globstar

    filenames=( /usr/lib*/**/lib*.a )

    for file in "${filenames[@]}"; do
      [[ -f $file ]] || continue
      echo "$file"
    done
}
----

Or, if you really want to use `find`, use `-print0` and an array:

[,bash]
----
func() {
    mapfile -t -d '' filenames < <(find /usr/lib* -type f -name 'lib*.a' -print0)
    for file in "${filenames[@]}"; do
      echo "$file"
    done
}
----

NOTE: `-d ''` is the same as `-d $'\0'` and sets the null byte as the delimiter.

or:

[,bash]
----
func() {
    find /usr/lib* -type f -name 'lib*.a' -print0 | while read -r -d '' file; do
      echo "$file"
    done
}
----

or

[,bash]
----
func() {
    while read -r -d '' file; do
      echo "$file"
    done < <(find /usr/lib* -type f -name 'lib*.a' -print0)
}
----

Use the tool options for null terminated strings, like `-print0`, `-0`, `-z`, etc.

== prefix or suffix array elements

Instead of:

[,bash]
----
func() {
  other-cmd $(for k in "$@"; do echo "prefix-$k"; done)
}
----

do

[,bash]
----
func() {
  other-cmd "${@/#/prefix-}"
}
----

or suffix:

[,bash]
----
func() {
  other-cmd "${@/%/-suffix}"
}
----

== Join array elements with a separator char

Here we have an associate array `_drivers`, where we want to print the keys separated by ',':

[,bash]
----
if [[ ${!_drivers[*]} ]]; then
    echo "rd.driver.pre=$(IFS=, ;echo "${!_drivers[*]}")" > "${initdir}"/etc/cmdline.d/00-watchdog.conf
fi
----

