190 lines
5.4 KiB
ReStructuredText
190 lines
5.4 KiB
ReStructuredText
|
: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
|
|||
|
}
|