2017-02-22 45 views
5

У меня довольно длинный сценарий bash, который вызывает довольно много внешних команд (git clone, wget, apt-get и др.), Которые печатают много материала на стандартный вывод.Как управлять многословием журналов внутри сценария оболочки?

Я хочу, чтобы у сценария было несколько вариантов подробностей, поэтому он печатает все из внешних команд, сводную версию (например, «Установка зависимостей ...», «Компиляция ...» и т. Д.) Или ничего вообще. Но как я могу это сделать без загромождения всего кода?

Я думал о возможных решениях для этого: нужно создать функцию-обертку, которая запускает внешние команды и печатает то, что необходимо для стандартного вывода, в зависимости от параметров, заданных в начале. Это проще реализовать, но это означает добавление большого количества лишнего беспорядка в код.

Другим решением является отправка всего вывода на пару внешних файлов и при анализе аргументов в начале скрипта, выполняющем tail -f в этом файле, если указана подробность. Это было бы очень легко реализовать, но мне кажется, что он довольно взломан, и я обеспокоен его влиянием на производительность.

Какой из них лучше? Я также открыт для других решений.

+1

Рассматривали ли вы с помощью 'установить -x '? Вызовите эту команду в верхней части вашего скрипта, и каждая последующая команда будет эхом. Вы можете отключить его с помощью 'set + x'. –

+1

Кроме того, вы можете использовать 'set -e', чтобы прервать скрипт, когда какая-либо команда выходит с ненулевым статусом. –

+0

Нет, это не то, что мне нужно. Я знаю, какие команды я запускаю, я просто хочу, чтобы мой скрипт распечатывал все, что они выводят, или нет, в зависимости от некоторых параметров. Это скорее вещь пользователя, чем фактический код. – Ocab19

ответ

4

У вас уже есть самая чистая идея в вашем вопросе (функция обертки), но вы, кажется, думаете, что это будет грязно. Я бы предложил вам пересмотреть. Это может выглядеть следующим образом (не обязательно полноценное решение, просто чтобы дать вам основную идею):

#!/bin/bash 

# Argument 1 : Logging level for that command 
# Arguments 2... : Command to execute 
# Output suppressed if command level >= current logging level 
log() 
{ 
if 
    (($1 >= logging_level)) 
then 
    "${@:2}" >/dev/null 2>&1 
else 
    "${@:2}" 
fi 
} 

logging_level=2 

log 1 command1 and its args 
log 2 command2 and its args 
log 3 command4 and its args 

Вы можете организовать для любого требуемого перенаправления (с дескрипторами файлов, если вы хотите), который будет обрабатываться в обертки, так что остальная часть скрипта остается читаемой и свободной от перенаправления и условий в зависимости от выбранного уровня ведения журнала.

+0

Я все еще не уверен, что добавление «журнала» к каждой команде, которую я запускаю, является самым чистым решением. Я беспокоюсь, что другим людям будет сложно читать код таким образом. но, кажется, нет лучшего способа, поэтому я думаю, что я поеду с этим в конце – Ocab19

+0

@ Ocab19 - посмотрите функции 'log_debug_file' и' log_verbose_file' в моем [ответе] (http://stackoverflow.com/а/42426680/6862601). С их помощью мы можем устранить потери помех/удобочитаемости, вызванные конструкцией, подобной «log 1» и т. Д., Префиксом каждой команды, выход которой нам нужно дросселировать. Но перенаправление вывода, а затем вызов функции журнала также загромождает. – codeforester

1

Решение 1. Рассмотрите возможность использования дополнительных файловых дескрипторов. Переназначение необходимых файловых дескрипторов в STDOUT или/dev/null в зависимости от выбранной многословности. Перенаправить вывод каждого оператора в ваш скрипт в дескриптор файла, соответствующий его значению. Посмотрите на https://unix.stackexchange.com/a/218355.

1

Решение 2.

Set $ ​​required_verbosity и трубы STDOUT каждого оператора в вашем сценарии к хелперов сценарий с двумя параметрами, что-то вроде этого:

заявление | logger actual_verbosity $ required_verbosity

В сценарии logger эхо STDIN в STDOUT (или файл журнала, что угодно), если $ actual_verbosity> = $ required_verbosity.

4

Улучшение на @ идею Фреда немного больше, мы могли бы построить небольшую лесозаготовительную библиотеку таким образом:

declare -A _log_levels=([FATAL]=0 [ERROR]=1 [WARN]=2 [INFO]=3 [DEBUG]=4 [VERBOSE]=5) 
declare -i _log_level=3 
set_log_level() { 
    level="${1:-INFO}" 
    _log_level="${_log_levels[$level]}" 
} 

log_execute() { 
    level=${1:-INFO} 
    if (($1 >= ${_log_levels[$level]})); then 
    "${@:2}" >/dev/null 
    else 
    "${@:2}" 
    fi 
} 

log_fatal() { ((_log_level >= ${_log_levels[FATAL]})) && echo "$(date) FATAL $*"; } 
log_error() { ((_log_level >= ${_log_levels[ERROR]})) && echo "$(date) ERROR $*"; } 
log_info() { ((_log_level >= ${_log_levels[INFO]})) && echo "$(date) INFO $*"; } 
log_debug() { ((_log_level >= ${_log_levels[DEBUG]})) && echo "$(date) DEBUG $*"; } 
log_verbose() { ((_log_level >= ${_log_levels[VERBOSE]})) && echo "$(date) VERBOSE $*"; } 

# functions for logging command output 
log_debug_file() { ((_log_level >= ${_log_levels[DEBUG]})) && [[ -f $1 ]] && echo "=== command output start ===" && cat "$1" && echo "=== command output end ==="; } 
log_verbose_file() { ((_log_level >= ${_log_levels[VERBOSE]})) && [[ -f $1 ]] && echo "=== command output start ===" && cat "$1" && echo "=== command output end ==="; } 

Скажет выше источник находится в файле библиотека под названием logging_lib.ш, мы могли бы использовать его в обычный скрипт так:

#!/bin/bash 

source /path/to/lib/logging_lib.sh 

set_log_level DEBUG 

log_info "Starting the script..." 

# method 1 of controlling a command's output based on log level 
log_execute INFO date 

# method 2 of controlling the output based on log level 
date &> date.out 
log_debug_file date.out 

log_debug "This is a debug statement" 
... 
log_error "This is an error" 
... 
log_fatal "This is a fatal error" 
... 
log_verbose "This is a verbose log!" 

приведет к этому выходу:

Fri Feb 24 06:48:18 UTC 2017 INFO Starting the script... 
Fri Feb 24 06:48:18 UTC 2017 
=== command output start === 
Fri Feb 24 06:48:18 UTC 2017 
=== command output end === 
Fri Feb 24 06:48:18 UTC 2017 DEBUG This is a debug statement 
Fri Feb 24 06:48:18 UTC 2017 ERROR This is an error 
Fri Feb 24 06:48:18 UTC 2017 FATAL This is a fatal error 

Как мы можем видеть, log_verbose не производит никакой продукции, так как уровень журнала находится на DEBUG, на одном уровне ниже VERBOSE. Тем не менее, log_debug_file date.out действительно выдавал вывод, а также log_execute INFO, так как уровень журнала установлен в DEBUG, что составляет> = INFO.

Используя это в качестве основы, мы могли бы также написать команду оберток, если нам нужно еще более тонкую настройку:

git_wrapper() { 
    # run git command and print the output based on log level 
} 

С этим на месте, сценарий может быть повышена принять аргумент --log-level level, который может определить log verbosity, с которым он должен работать.


Если кому-то интересно, почему некоторые переменные названы с подчеркивания в коде выше, увидеть этот пост:

+0

Это здорово, когда мы получаем мысли @ charlesduffy об этом. – codeforester

Смежные вопросы