nixhacks.net/content/posixish_functions.rst

190 lines
5.4 KiB
ReStructuredText
Raw Normal View History

2022-10-21 22:29:23 +03:00
:title: Несколько полезных функций на Bourne Shell
:date: 18 Oct 22
==========================================
Несколько полезных функций на Bourne Shell
==========================================
Функции, описанные здесь можно применять в любой POSIX-совместимой оболочке.
Протестировано в **dash**. Примеры на этой странице будут обновляться.
**1. Парсер аргументов в стиле GNU**
.. code-block:: shell
optval() {
# GNU-style command line options parser
#
# Usage: optval "$1" "$2"
#
# Set variables:
# OPT - option name
# VAL - value
# SFT - value for shift command
OPT="${1%%=*}"; VAL="${1#*=}"; SFT=1
if [ "$OPT" = "$VAL" ]; then
if [ -n "$2" ] && [ "${2#"${2%%?}"}" != "-" ]; then
VAL="$2"; SFT=2
else
unset VAL
fi
fi
if [ -z "$VAL" ]; then
echo Missing argument for option "$OPT" >&2
exit 1
fi
}
Эта функция позволит надёжно распарсить опции с обязательным аргументом. Ей
по силам:
* **-o** *value*
* **--option**\ =\ *value*
* **--option**\ =\ *name=value*
* **-option** *value*
..и вариации.
Непосредственно в POSIX-оболочке работать с парсером будет тяжко из-за
отсутсвия массивов. Единственный доступный массив **$@** здесь занят. NULL в
качестве разделителя для списка позиционных аргументов использовать не
получится, поэтому ниже пример с двоеточием. В **Bash** таких проблем нет.
.. code-block:: shell
while [ "$#" -ne 0 ]; do
case "$1" in
-h|--help)
echo "Usage: $0 [-chv] [--] arguments"
exit 0
;;
-v|--version)
echo 1.0.0
exit 0
;;
-c|-c=*|--config|--config=*)
optval "$1" "$2"
config="$VAL"
shift "$SFT"
;;
--) # end of options processing
shift
break
;;
-*)
echo Unknown option "$1" >&2
exit 1
;;
*) # Save positional arguments
# In Bash better use ARGS+=("$1") instead
ARGS="$1":"$ARGS"
shift
esac
done
# Set positional arguments. It is not needed in Bash
# In Bash you can use ARGS array directly or use set -- "${ARGS[@]}"
OLDIFS="$IFS"; IFS=:
# $arg must be unquoted!
# shellcheck disable=SC2086
for arg in $ARGS; do set -- $arg "$@"; done
IFS="$OLDIFS"
echo Config: "$config"
echo Positional arguments:
for arg in "$@"; do; echo : "$arg"; done
**2. Интерактивный диалог**
.. code-block:: shell
confirm() {
# Confirmation interactive dialog
#
# Usage: confirm "message"
[ -n "$ASSUME_NO" ] && return 1
[ -n "$ASSUME_YES" ] && return 0
while true; do
printf '%s (y/n) ' "$*"
read -r REPLY
case "$REPLY" in
[Yy]|[Yy][Ee][Ss]) return 0;;
[Nn]|[Nn][Oo]) return 1;;
*) echo Please, answer y or n;;
esac
done
}
Здесь предусмотрены переменные ``ASSUME_NO`` и ``ASSUME_YES`` для
неинтерактивного режима. Автоматическое "Нет" и "Да" соответственно. Пример:
.. code-block:: shell
if confirm 'Proceed?'; then
echo OK
else
echo Abort
fi
**3. Версия Python**
Просто напечатает в STDOUT версию интерпретатора Python.
.. code-block:: shell
python_version() {
# Get Python interpreter version in format 'major.minor.micro' e.g. '3.10.2'
# Works with Python 2.x and 3.x.
#
# Usage: python_version [<path to interpreter>]
"${1:-python}" -c "import sys; v=sys.version_info; \
print('%s.%s.%s' % (v.major, v.minor, v.micro))"
}
**4. Открыть редактор по умолчанию**
.. code-block:: shell
default_editor() {
# Edit file in user's default editor
#
# Usage: default_editor file
if [ -n "$EDITOR" ]; then
"$EDITOR" "$@"
return 0
fi
if hash select-editor >/dev/null 2>&1; then
if [ -f ~/.selected_editor ]; then
. ~/.selected_editor
else
select-editor
. ~/.selected_editor
fi
"$SELECTED_EDITOR" "$@"
return 0
fi
if hash editor >/dev/null 2>&1; then
editor "$@"
return 0
else
vi "$@" # fallback to vi
return 0
fi
}
**5. Простой URL-decoder**
.. code-block:: shell
unescape_uri() {
echo "$1" | sed 's/%/\\\\x/g' | xargs printf
}