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
|
||
}
|