3.6 KiB
BASH Notes
basename
Don't use basename
, use:
file=${path##*/}
dirname
Don't use dirname
, use:
dir=${path%/*}
shopt
If you set shopt
in a function, reset to its default state with trap
:
func() {
trap "$(shopt -p nullglob globstar)" RETURN
shopt -q -s nullglob 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:
func() {
for file in $(find /usr/lib* -type f -name 'lib*.a' -print0 ); do
echo $file
done
}
use:
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:
func() {
trap "$(shopt -p nullglob globstar)" RETURN
shopt -q -s nullglob 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:
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:
func() {
find /usr/lib* -type f -name 'lib*.a' -print0 | while read -r -d '' file; do
echo "$file"
done
}
or
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:
func() {
other-cmd $(for k in "$@"; do echo "prefix-$k"; done)
}
do
func() {
other-cmd "${@/#/prefix-}"
}
or suffix:
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 ',':
if [[ ${!_drivers[*]} ]]; then
echo "rd.driver.pre=$(IFS=, ;echo "${!_drivers[*]}")" > "${initdir}"/etc/cmdline.d/00-watchdog.conf
fi
Optional parameters to commands
If you want to call a command cmd
with an option, if a variable is set, rather than doing:
func() {
local param="$1"
if [[ $param ]]; then
param="--this-special-option $param"
fi
cmd $param
}
do it like this:
func() {
local param="$1"
cmd ${param:+--this-special-option "$param"}
}
# cmd --this-special-option 'abc'
func 'abc'
# cmd
func ''
# cmd
func
If you want to specify the option even with an empty string do this:
func() {
local -a special_params
if [[ ${1+_} ]]; then
# only declare `param` if $1 is set (even as null string)
local param="$1"
fi
# check if `param` is set (even as null string)
if [[ ${param+_} ]]; then
special_params=( --this-special-option "${param}" )
fi
cmd ${param+"${special_params[@]}"}
}
# cmd --this-special-option 'abc'
func 'abc'
# cmd --this-special-option ''
func ''
# cmd
func
Or more simple, if you only have to set an option:
func() {
if [[ ${1+_} ]]; then
# only declare `param` if $1 is set (even as null string)
local param="$1"
fi
cmd ${param+--this-special-option}
}
# cmd --this-special-option
func 'abc'
# cmd --this-special-option
func ''
# cmd
func