-
Notifications
You must be signed in to change notification settings - Fork 0
Shell snippets
Code is mostly BASH-style, compatibility with plain Bourne Shell is not guaranteed!
if [ "$_" = /bin/bash ]; then
echo "Script is exec-ed"
elif [ "$0" = "$BASH_SOURCE" ]; then
echo "Script is subshell"
else
echo "Script is sourced"
fiif [[ $_ != $0 ]]
then
echo "Executing instead of source!" >&2
"${BASH_SOURCE[0]}" "$@"
return
fi
die()
{
[ -n "$1" ] && echo "$1" >&2;
exit 1
}
die()
{
[ -n "$1" ] && echo "$1" >&2;
if [[ $_ != $0 ]]
then
while true; do kill -SIGINT -$$; sleep 700d; done
else
exit 1
fi
}
IFS=""
shopt -s expand_aliases
alias read='IFS=" " read'
Useful for avoiding every-time protection of variables with double quotes (i.e vars as single arguments):
$ IFS=""
$ f() { echo $1; }; x="a b c"; f $x
a b c
To return to default behavior unset IFS:
$ unset IFS
$ f() { echo $1; }; x="a b c"; f $x
a
Check foo.sh for usage ready bash script starter with inline documentation of options and some utility functions.
#!/bin/bash
optstring_long="foo,bar:,baz::,verbose,dry-run,help"
optstring_short="fb:z::vnh"
opts=$(getopt -o "${optstring_short}" --long "${optstring_long}" --name "$0" -- "$@") ||
exit $?
eval set -- "$opts"
unset foo
unset bar
unset baz
unset verbose_on
unset verbose_off
unset verbose
unset dryrun
while true
do
case "$1" in
-f|--foo)
foo=true
echo "Foo!"
shift;;
-b|--bar)
bar=$2
echo "Bar: ${bar}"
shift 2;;
-z|--baz)
baz=${2:-default!}
echo "Baz: ${baz}"
shift 2;;
-v|--verbose)
verbose_on="set -x"
verbose_off="{ set +x; } 2>/dev/null"
verbose=verbose_run
shift;;
-n|--dry-run)
dryrun=echo
shift;;
-h|--help)
cat <<EOF
This is help!
EOF
exit;;
--) shift; break;;
esac
done
verbose_run() { set -x; eval "$@"; { set +x; } 2>/dev/null; }require_param()
{
local param
local missed=""
for param in "$@"
do
eval local val=\$$param
[ -z "$val" ] && {
missed="$missed --$param"
}
done
[ -n "$missed" ] &&
die "Required parameter(s) missed:${missed}"
}optional_arg()
{
eval local next_token=\$$((OPTIND + 1))
if [[ -n $next_token && $next_token != -* ]]; then
OPTIND=$((OPTIND + 1))
OPTARG=$next_token
[[ -n "$1" ]] &&
eval $1=\$next_token
else
OPTARG=""
fi
}$ sed -r -e 's/(.)/-\1|/g;s/^(.*)\|$/+(\1)/' <<< abcde
+(-a|-b|-c|-d|-e)Use in combination with shopt -s extglob.
[[ ":$PATH:" == *":${dir}:"* ]] &&
echo 'Yes!'tmp=":${PATH}:"
tmp=${tmp/:${dir}:/:}
tmp=${tmp%:};
PATH=${tmp#:}
add_path()
{
local v=$1
shift
local back=false
if [ "$v" = _back ]; then
v=$1
shift
back=true
fi
while [ "$1" ]; do
if eval [[ ":\$$v:" == *\"":${1}:"\"* ]]; then
shift
continue
fi
if $back; then
eval export $v=\$$v:\$1
else
eval export $v=\$1:\$$v
fi
shift
done
}Example:
add_path PATH "${opt}/bin" "${bush_dir}" "${bush_dir}/bin"
add_path _back CDPATH "${src[0]}" "${src[1]}"push_back()
{
arr=$1; shift
for val in "$@"
do
eval $arr[\${#$arr[@]}]=\$val
done
}
# Usage:
# your_array=()
# push_back your_array value1 [value2] ...
# echo "${your_array[@]}"
check_empty()
{
shopt -s nullglob # to not croak on empty expansion
shopt -s dotglob # to include hidden files
local -a files=(${1:-.}/*)
shopt -u nullglob dotglob
if [ "$files" ]
then
return 1
else
return 0
fi
}
ask_yes_no()
{
local REPLY
while true; do
read -n1 ${1:+-p $@}
echo
case $REPLY in
[Yy]) return 0;;
[Nn] ) return 1;;
esac
done
}
script=$(readlink -ne "$0")
backtrace ()
{
echo "Backtrace is:"
i=0
while caller $i
do
i=$((i+1))
done
}
#!/bin/bash
run()
{
echo executing: $BASH_COMMAND
}
trap run DEBUG
a=1
while true
do
echo h
exit
done
Result:
executing: a=1
executing: true
executing: echo h
h
executing: exit
$ date '+%b %e %H:%M:%S Hello world!'
Jul 2 13:54:41 Hello world!
$ date '+%F %H:%M:%S Bye hell!'
2015-03-05 13:28:55 Bye hell!
$ date '+timestamped_%Y%m%d_%H%M%S'
timestamped_20171128_114901
Redirect output to it, each 100 lines a dot will be printed. Supply it initial message as first argument.
show_progress()
{
t=$1
case "$option_verbose" in
y*) cat;;
*)
i=0
step=100
while read
do
if ((i > step))
then
i=0
echo -n ${t}.
unset t
else
((i++))
fi
done
[ -z "$t" ] && echo done!
;;
esac
}
kill $(jobs -p)
at_exit()
{
kill $(jobs -p)
}
trap at_exit EXIT
word=\'${word//\'/\'\"\'\"\'}\'
grep -obaUP '\xfc\x4e' /dev/sda1
66649758:üN
67538791:üN
...
Broken? Not confirmed by hexdump. xxd is broken (shows all 00 instead of data).
LC_ALL=C
bin2hex()
{
# FIXME: this cannot read zero-byte, it is treated as EOF
while read -n 1 byte
do
printf "%x" "'$byte"
done
printf "\n"
}
- Using
localwhen declaring/assigning vars inside functions will make them local -
<<<for expressions works like<<for files -
declare -Amakes associative arrays (no another way) - Always remember that when you create pipeline, you create sub-process:
$ what=no && echo yes| read what && echo $what
no
Don't believe that read works?
$ what=no && echo yes| (read what && echo $what)
yes
It is possible to use read in current process:
$ what=no && read what <<< yes && echo $what
yes
Or even with catching sub-process output:
$ what=no && read what <<< "$(echo yes)" && echo $what
yes
- [ expr1 -a expr2 ] is faster (~15% CPU) equivalent of [ expr1 ] && [ expr2 ]
- Don't forget about trailing
/in path intersection checks:
dir_a=/abc/def
dir_b=/abc
[[ $dir_a/ == $dir_b/* ]] &&
echo yes
[[ $dir_a == $dir_b/* ]] ||
echo no
[[ $dir_a// == $dir_b/* ]] &&
echo yes
Result:
yes
no
yes
Note, that count of / does not matter. So, you should not worry if / termination in variable.
-
command_not_found_handle()sub will catch command not found errors in Bash 4.