: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 [] "${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 }