Как я могу печатать только определенные команды из скрипта bash во время их выполнения?

19

У меня есть скрипт bash с различными операторами if, основанными на аргументах командной строки, которые я передаю при его вызове. Наличие некоторого вывода о том, какие команды выполняются, полезно для подтверждения прохождения всех этих операторов if, но мое текущее решение дает мне слишком много информации.

Использование set -vв сценарии было несколько полезно, чтобы увидеть команды, выводимые на экран, так как они выполнялись в сценарии, однако я получаю слишком много команд. Это почти как полная копия сценария.

Я хочу вывод, который показывает, какие команды выполняются, но я не хочу видеть комментарии, новые строки, выражения в операторах if и т. Д.

Есть ли способ, которым я могу передать все возможные выходные данные, генерируемые параметром -v, через регулярное выражение перед печатью? Или какое-то другое решение, позволяющее bash выводить только команды определенного «типа» (например, использующие исполняемые файлы, а не только определенные операторы bash, комментарии и т. Д.?)

[1] /programming/257616/sudo-changes-path-why был весьма полезен в этом вопросе и именно здесь я получил предложение об set -vиспользовании.

Редактировать :

Сценарий, похожий (но не идентичный) на тот, который я запускаю:

#!/bin/bash

#get verbose command output
set -v

env=$1

if [ "$env" == "dev" ]; then
    python ascript.py
fi

if [ "$env" == "prod" ]; then

    #launching in prod will most likely fail if not run as root. Warn user if not running as root.
    if [ $EUID -ne 0 ]; then
        echo "It doesn't look like you're running me as root. This probably won't work. Press any key to continue." > /dev/stderr
        read input
    fi

    #"stop" any existing nginx processes
    pkill -f nginx

    nginx -c `pwd`/conf/artfndr_nginx.conf

fi

Я хочу только 2 возможных набора выходных строк из этого скрипта. Первый:

python ascript.py

Секунда:

pkill -f nginx
nginx -c /some/current/directory/conf/artfndr_nginx.conf
Trindaz
источник
1
Конечно, вы можете разобрать его, но мы не сможем помочь, если вы не покажете нам сценарий и не объясните, какие части set -vвывода вам нужны, а какие нет.
Terdon

Ответы:

12

Когда я пишу более сложные сценарии bash, я использую небольшую функцию для запуска команд, которая также выводит команды, запущенные в лог-файл:

runthis(){
    ## print the command to the logfile
    echo "$@" >> $LOG
    ## run the command and redirect it's error output
    ## to the logfile
    eval "$@" 2>> $LOG 
}

Затем в моем скрипте я запускаю команды, подобные этой:

runthis "cp /foo/bar /baz/"

Если вы не хотите печатать команду, просто запустите ее как обычно.

Вы можете установить $LOGимя файла или просто удалить его и распечатать в stdout или stderr.

Тердон
источник
+1 Также я смог запустить это в своем скрипте, просто добавив «важные» команды с коротко названной версией функции, так что строки выглядят примерно так, v python ascript.pyбез необходимости заключать в кавычки и терять подсветку кода vim
Trindaz
@Trindaz кавычки существуют для случаев, когда вам нужно передать переменные в ваших командах, если переменные содержат пробелы, у вас могут возникнуть проблемы в противном случае.
Terdon
eval ..... || ok=1: установит нормально "1", только когда произойдет сбой "eval ..." ?? Может быть, вы имели в виду "&&"? И если вы это имели в виду, добавьте «ok = 0» перед строкой eval, чтобы она «сбрасывалась» каждый раз. Или просто переименовать «ок» в «ошибка»? кажется, это то, что здесь подразумевалось. Итак, в итоге:eval "$@" 2>> "$LOG" && error=0 || error=1
Оливье Дюлак
@OlivierDulac, в версии, которую я использую, у меня есть okпеременная, которая остановит скрипт, если какая-либо команда завершится неудачно. Так как это не имеет значения здесь, я удалил его, но забыл удалить || ok=1. Спасибо, исправлено.
Terdon
Отличное решение! Мне пришлось удалить "окружающий оператор eval, потому что команда уже окружена "s
gromit190
11

Используйте вложенную оболочку, то есть:

( set -x; cmd1; cmd2 )

Например:

( set -x; echo "hi there" )

печать

+ echo 'hi there'
hi there
Nik
источник
Я предпочитаю этот set -x; cmd; set +xпо нескольким причинам. Во-первых, он не сбрасывается, set -xесли он был включен ранее. Во-вторых, завершение скрипта внутри не приводит к выполнению ловушек с подробными настройками.
Оливер Гонджа
2

Я видел методы, похожие на @ terdon's. Это начало того, что языки программирования более высокого уровня называют регистраторами и предлагают как полноценные библиотеки, такие как log4J (Java), log4Perl (Perl) и т. Д.

Вы можете получить что-то похожее, используя set -xBash, как вы уже упоминали, но вы можете использовать это, чтобы включить отладку только для подмножества команд, обернув блоки кода с ними примерно так.

$ set -x; cmd1; cmd2; set +x

Примеры

Вот один шаблон лайнера, который вы можете использовать.

$ set -x; echo  "hi" ;set +x
+ echo hi
hi
+ set +x

Вы можете обернуть их так для нескольких команд в скрипте.

set -x
cmd1
cmd2
set +x

cmd3

Log4Bash

Большинство людей не замечают, но у Bash также есть log4 *, Log4Bash . Если у вас более скромные потребности, возможно, стоит потратить время на его настройку.

log4bash - это попытка улучшить ведение журнала для сценариев Bash (т. е. сделать так, чтобы вход в Bash меньше занимал).

Примеры

Вот несколько примеров использования log4bash.

#!/usr/bin/env bash
source log4bash.sh

log "This is regular log message... log and log_info do the same thing";

log_warning "Luke ... you turned off your targeting computer";
log_info "I have you now!";
log_success "You're all clear kid, now let's blow this thing and go home.";
log_error "One thing's for sure, we're all gonna be a lot thinner.";

# If you have figlet installed -- you'll see some big letters on the screen!
log_captains "What was in the captain's toilet?";

# If you have the "say" command (e.g. on a Mac)
log_speak "Resistance is futile";

Log4sh

Если вы хотите то, что я бы классифицировал как большую часть всей инфраструктуры log4 *, я бы попробовал Log4sh .

выдержка

Первоначально log4sh был разработан для решения проблемы с журналированием, которая возникала в некоторых производственных средах, в которых я работал, где у меня либо было слишком много журналирования, либо недостаточно. В частности, работа в Cron доставила мне наибольшую головную боль из-за их постоянных и раздражающих электронных писем, в которых говорилось, что все работает, или что ничего не работает, но нет подробной причины, почему. Теперь я использую log4sh в средах, где регистрация из сценариев оболочки имеет решающее значение, но где мне нужно больше, чем просто «Здравствуйте, исправьте меня!» тип сообщения журнала. Если вам нравится то, что вы видите, или у вас есть предложения по улучшению, пожалуйста, напишите мне по электронной почте. Если интерес к проекту будет достаточно, я буду его развивать.

log4sh был разработан в рамках Bourne Again Shell (/ bin / bash) в Linux, но была предпринята большая осторожность, чтобы убедиться, что он работает под стандартной оболочкой Bourne Shell из Solaris (/ bin / sh), так как это является основной продукцией Платформа используется мной.

Log4sh поддерживает несколько оболочек, а не только Bash.

  • Борн Шелл (ш)
  • BASH - GNU Bourne Again Sheell (Баш)
  • Тире (тире)
  • Корн Шелл (кш)
  • pdksh - общественное достояние Korn Shell (pdksh)

Он также был протестирован на нескольких ОС, а не только на Linux.

  • Cygwin (под Windows)
  • FreeBSD (поддерживается пользователем)
  • Linux (Gentoo, RedHat, Ubuntu)
  • Mac OS X
  • Солярис 8, 9, 10

Использование каркаса log4 * потребует некоторого времени для изучения, но оно того стоит, если у вас есть более требовательные потребности в вашей регистрации. Log4sh использует файл конфигурации, в котором вы можете определить приложения и управлять форматированием для вывода, который будет отображаться.

пример

#! /bin/sh
#
# log4sh example: Hello, world
#

# load log4sh (disabling properties file warning) and clear the default
# configuration
LOG4SH_CONFIGURATION='none' . ./log4sh
log4sh_resetConfiguration

# set the global logging level to INFO
logger_setLevel INFO

# add and configure a FileAppender that outputs to STDERR, and activate the
# configuration
logger_addAppender stderr
appender_setType stderr FileAppender
appender_file_setFile stderr STDERR
appender_activateOptions stderr

# say Hello to the world
logger_info 'Hello, world'

Теперь, когда я запускаю это:

$ ./log4sh.bash 
INFO - Hello, world

ПРИМЕЧАНИЕ. Приведенное выше конфигурирует appender как часть кода. При желании это можно извлечь в собственный файл и log4sh.propertiesт. Д.

Обратитесь к отличной документации для Log4sh, если вам нужна дополнительная информация.

SLM
источник
Спасибо за добавленные заметки, но главная проблема, с которой я столкнулся, - это все setкоманды, которые мне нужно ввести, чередование комментариев и т. Д., Поэтому просто наличие функции в верхней части моего скрипта с односимвольным вызовом функции, предшествующим все "важные" строки в сценарии мне пока казались более аккуратными. (один символ, потому что функция имеет одно имя символа)
Trindaz
@Trindaz - извините, я еще не закончил свой ответ. Посмотрите на log4bash, если у вас больше потребностей, чем у функции, которую дал тердон.
SLM
1
@Trindaz - я делаю что-то подобное время от времени, другой подход, который я использовал, - это обернуть echoсвою собственную функцию mecho, а затем передать переключатель в программу, вызываемую -vдля многословия, когда я хочу выключить вещи. Я также могу управлять им с помощью переключателя 2-го аргумента, который задает имя функции, поэтому у меня есть 2 оси для управления журналированием. Это часто - ворота к желанию log4bash все же.
SLM
1
@Trindaz set -xпечатает команды по мере их выполнения. Он не печатает комментарии. set -xпрактично для отладки (в отличие от set -vчего не очень полезно). Zsh имеет лучший результат, set -xчем bash, например, он показывает, какая функция выполняется в данный момент, и номер строки источника.
Жиль "ТАК - перестань быть злым"
Спасибо @Gilles, это правда, но это дало мне расширение выражений if, которое в данном случае было излишним
Trindaz
1

Вы могли бы , trap DEBUGа затем проверить на BASH_COMMANDпеременную . Добавьте это в начало скрипта:

log() {
    case "$1" in
        python\ *)
            ;&
        pkill\ *)
            printf "%s\n" "$*"
            ;;
    esac
}

trap 'log "$BASH_COMMAND"' DEBUG

Код читабелен; он просто проверяет, начинается ли первый аргумент с pythonили pkill, и печатает его, если это так. И ловушка использует BASH_COMMAND(которая содержит команду, которая будет выполнена) в качестве первого аргумента.

$ bash foo.sh dev
python ascript.py
python: can't open file 'ascript.py': [Errno 2] No such file or directory
$ bash foo.sh prod
It doesn't look like you're running me as root. This probably won't work. Press any key to continue.

pkill -f nginx
foo.sh: line 32: nginx: command not found

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

if [[ $1 =~ python|nginx ]]
then
    printf "%s" "$*"
fi

И используйте регулярные выражения.

Мур
источник
0

Это пересмотренная версия аккуратной функции Стивена Пенни. Он печатает свои аргументы в цвете и цитирует их по мере необходимости. Используйте его для выборочного отображения команд, которые вы хотите отследить. Поскольку кавычки выводятся, вы можете скопировать напечатанные строки и вставить их в терминал для немедленного повторного выполнения во время отладки скрипта. Прочтите первый комментарий, чтобы узнать, что я изменил и почему.

xc() # $@-args
{
  cecho "$@"
  "$@"
}
cecho() # $@-args
{
  awk '
  BEGIN {
    x = "\047"
    printf "\033[36m"
    while (++i < ARGC) {
      if (! (y = split(ARGV[i], z, x))) {
        printf (x x)
      } else {
        for (j = 1; j <= y; j++) {
          printf "%s", z[j] ~ /[^[:alnum:]%+,./:=@_-]/ ? (x z[j] x) : z[j]
          if (j < y) printf "\\" x
        }
      }
      printf i == ARGC - 1 ? "\033[m\n" : FS
    }
  }
  ' "$@"
}

Пример использования с выводом:

# xc echo "a b" "c'd" "'" '"' "fg" '' " " "" \# this line prints in green

echo 'a b' c\'d \' '"' fg '' ' ' '' '#' this line prints in green

a b c'd ' " fg # this line prints in green

Вторая строка выше печатается зеленым цветом и может быть скопирована для воспроизведения третьей строки.

Дальнейшие замечания

Оригинальный xc @ Steven-Penny умный, и он заслуживает всех кредитов за это. Однако я заметил некоторые проблемы, но я не мог прокомментировать его пост напрямую, потому что у меня недостаточно репутации. Поэтому я предложил изменить его пост, но рецензенты отклонили его. Поэтому я решил опубликовать свои комментарии в качестве этого ответа, хотя я бы предпочел иметь возможность редактировать собственный ответ Стива Пенни.

Что я изменил в ответе Стивена-Пенни

Исправлено: печать пустых строк - они не печатались. Исправлено: печать строк, которые включают %- они вызывали ошибки синтаксиса awk. Заменено for (j in ...)на, for (j = 0, ...)потому что первое не гарантирует порядок обхода массива (это зависит от реализации awk). Добавлены 0 к восьмеричным числам для переносимости.

Обновить

Стивен Пенни с тех пор исправил эти проблемы в своем ответе, поэтому эти замечания остаются только для исторической записи моего ответа. Смотрите раздел Комментарии для получения более подробной информации.

stepse
источник
0

Вы можете использовать функцию оболочки "sh_trace" из библиотеки POSIX stdlib, чтобы напечатать команду в цвете перед ее запуском. Пример:

предварительный просмотр

Основная функция Awk:

function sh_trace(ary,   b, d, k, q, w, z) {
  b = "\47"
  for (d in ary) {
    k = split(ary[d], q, b)
    q[1]
    if (d - 1)
      z = z " "
    for (w in q) {
      z = z (!k || q[w] ~ "[^[:alnum:]%+,./:=@_-]" ? b q[w] b : q[w]) \
      (w < k ? "\\" b : "")
    }
  }
  printf "\33[36m%s\33[m\n", z
  system(z)
}
Стивен Пенни
источник