Простая функция журнала в Bash

Мне просто становится немного удобнее с Bash.

Мне нужна была очень простая функция журнала для записи в файл и вывода на консоль сообщений и ошибок. Я также хотел, чтобы терминал и файл интерпретировали символы новой строки, а не распечатывали их. Вот что я придумал (отлично работает):

#!/bin/bash

log () {
  if [[ "$3" == '-e' || "$3" == '--error' ]]; then
    >&2 echo -e "ERROR: $1" && printf "ERROR: $1n" >> "$2"
  else
    echo -e "$1" && printf "$1n" >> "$2"
  fi
}

# Example implementation
log_file=snip/init.log
msg="nthis is an error message with leading newline character"
log "$msg" $log_file -e
msg="this is success message with no newline characters"
log "$msg" $log_file

Мне больше не нужна функциональность, но у меня есть вопросы:

  1. Является ли эффективный и читаемый фрагмент кода, который не сломается при передаче строк со специальными символами?
  2. Синтаксически, мог бы я сделать это «лучше», например, использовать трубы вместо &&. tee вместо того printf так далее.?
  3. Как я могу реорганизовать этот код, чтобы разрешить необязательный -e флаг, который нужно передать первым?

1 ответ
1

Здесь довольно много повторений:

  • echo && print примерно с теми же аргументами – tee наверное лучше здесь
  • пути с ошибками и без ошибок – возможно, используйте одну функцию, чтобы добавить ERROR: отметьте и позвоните другому?

Может быть, лучше сначала указать имя файла, а затем любое количество слов сообщения? Это сделало бы интерфейс более похожим на echo, Например.

Также есть проблема с нашим использованием printf – Любые % символы в сообщении об ошибке будут интерпретированы как индикаторы замены. Не то, что мы хотим. Мы могли бы использовать printf %b вместо этого развернуть escape-последовательности:

# log FILE MESSAGE...
log () {
    local file="$1"; shift
    printf '%b ' "$@" 'n' | tee -a "$file"
}

# log_err FILE MESSAGE...
log_error () {
    local file="$1"; shift
    local message="ERROR: $1"; shift
    log "$file" "$message" "$@" >&2
}

Если мы действительно хотим иметь единственную функцию с аргументами (например, для добавления дополнительных опций), тогда мы можем захотеть использовать while/case подход к синтаксическому анализу опций, который часто используется на программном уровне:

#!/bin/bash

set -eu

# log [-e] [-f FILE] MESSAGE...
log() {
    local prefix=""
    local stream=1
    local files=()
    # handle options
    while ! ${1+false}
    do case "$1" in
        -e|--error) prefix="ERROR:"; stream=2 ;;
        -f|--file) shift; files+=("${1-}") ;;
        --) shift; break ;; # end of arguments
        -*) log -e "log: invalid option '$1'"; return 1;;
        *) break ;; # start of message
       esac
       shift
    done
    if ${1+false}
    then log -e "log: no message!"; return 1;
    fi
    # if we have a prefix, update our argument list
    if [ "$prefix" ]
    then set -- "$prefix" "$@"
    fi
    # now perform the action
    printf '%b ' "$@" 'n' | tee -a "${files[@]}" >&$stream
}

Здесь есть неочевидный трюк, который может потребовать объяснения: я использовал ${1+false} расширить до false когда остался хотя бы один аргумент, или пустой команде, когда аргументов нет (пустая команда считается “истинной” if и while).

  • Я думаю, мне нужно сохранить этот код в одной функции, хотя, поскольку у меня будет еще одна функция журнала для ведения журнала только в файл (без консоли), я вызываю эту функцию log_silent. Дело в том, что мне не нужны 4 функции для выполнения этой работы. Я хотел бы сделать это с помощью всего двух функций.

    – только

  • 1

    Да, local и tee -a и новая строка – все это хорошие улучшения (я должен был попробовать код перед публикацией!). Отредактировано.

    – Тоби Спейт


  • 1

    Я обновился, чтобы показать, как можно анализировать параметры в одной функции. Должно быть легко добавить параметр “без звука”, который устанавливает stream=/dev/null.

    – Тоби Спейт

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *