Jak mogę wydrukować tylko niektóre polecenia ze skryptu bash podczas ich uruchamiania?

19

Mam skrypt bash z różnymi instrukcjami if opartymi na argumentach wiersza poleceń, które przekazuję podczas jego wywoływania. Posiadanie pewnego rodzaju danych wyjściowych dotyczących uruchamianych poleceń jest pomocne w potwierdzeniu przepływu przez wszystkie instrukcje if, ale moje obecne rozwiązanie daje mi zbyt wiele informacji.

Używanie set -vw skrypcie było nieco pomocne w wyświetlaniu poleceń drukowanych na ekranie podczas ich uruchamiania w skrypcie, jednak dostaję zbyt wiele poleceń. To prawie jak cała kopia skryptu.

Chcę danych wyjściowych, które pokazują, jakie polecenia są wykonywane, ale nie chcę widzieć komentarzy, nowych wierszy, wyrażeń w instrukcjach if itp.

Czy istnieje sposób, aby przed wydrukowaniem przekazać wszystkie możliwe dane wyjściowe wygenerowane przez opcję -v przez wyrażenie regularne? Lub jakieś inne rozwiązanie, aby uzyskać bash tylko do poleceń wyjściowych określonego „typu” (np. Które używają plików wykonywalnych, a nie tylko konkretnych poleceń, komentarzy itp.?)

[1] /programming/257616/sudo-changes-path-why był bardzo pomocny w tym i dostałem propozycję set -vużycia.

Edytuj :

Podobny (ale nie identyczny) skrypt do tego, który uruchamiam:

#!/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

Chcę tylko 2 możliwe zestawy linii wyjściowych z tego skryptu. Pierwszy:

python ascript.py

Drugi:

pkill -f nginx
nginx -c /some/current/directory/conf/artfndr_nginx.conf
Trindaz
źródło
1
Oczywiście możesz to przeanalizować, ale nie możemy pomóc, chyba że pokażesz nam skrypt i wyjaśnisz, które części set -vwyniku chcesz, a które nie.
terdon

Odpowiedzi:

12

Kiedy piszę bardziej złożone skrypty bash, używam małej funkcji do uruchamiania poleceń, które również wypisują polecenia uruchamiane do pliku dziennika:

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

Następnie w moim skrypcie uruchamiam takie polecenia:

runthis "cp /foo/bar /baz/"

Jeśli nie chcesz drukować polecenia, po prostu uruchom je normalnie.

Możesz ustawić $LOGnazwę pliku lub po prostu go usunąć i wydrukować na stdout lub stderr.

terdon
źródło
+1 Również mogłem uruchomić to w swoim skrypcie, po prostu przygotowując „ważne” polecenia za pomocą krótkiej wersji funkcji, dzięki czemu linie wyglądają mniej więcej tak, że v python ascript.pynie
muszę zawierać
@Trindaz cytaty są dostępne, gdy trzeba przekazać zmienne w swoich poleceniach, jeśli zmienne zawierają spacje, w przeciwnym razie możesz mieć problemy.
terdon
eval ..... || ok=1: ustawi ok na „1” tylko wtedy, gdy „eval ...” nie powiedzie się ?? Może miałeś na myśli „&&”? A jeśli miałeś na myśli, dodaj „ok = 0” przed linią eval, aby za każdym razem „resetować”. Lub po prostu zmień nazwę „ok” na „błąd”? wydaje się, że o to tu chodziło. Tak więc na koniec:eval "$@" 2>> "$LOG" && error=0 || error=1
Olivier Dulac
@OlivierDulac, w wersji, której używam, mam okzmienną, która zatrzyma skrypt, jeśli jakiekolwiek polecenie zawiedzie. Ponieważ nie miało to tutaj znaczenia, usunąłem go, ale zapomniałem usunąć || ok=1. Dzięki, naprawione teraz.
terdon
Doskonałe rozwiązanie! Musiałem usunąć "otaczającą instrukcję eval, ponieważ polecenie jest już otoczone przez "s
gromit190
11

Użyj podpowłoki, tj .:

( set -x; cmd1; cmd2 )

Na przykład:

( set -x; echo "hi there" )

odbitki

+ echo 'hi there'
hi there
Nik
źródło
Wolę ten jeden z set -x; cmd; set +xkilku powodów. Po pierwsze, nie resetuje się set -xw przypadku, gdy był wcześniej włączony. Po drugie, zakończenie skryptu wewnątrz nie powoduje, że pułapki są wykonywane przy ustawionych szczegółach.
Oliver Gondža
2

Widziałem stosowane metody podobne do @ terdona. To początki tego, które języki programowania wyższego poziomu wywołują rejestratory i oferują jako pełnowymiarowe biblioteki, takie jak log4J (Java), log4Perl (Perl) itp.

Możesz dostać coś podobnego używając set -xBash, jak wspomniałeś, ale możesz go użyć, aby podkręcić debugowanie tylko podzbioru poleceń, owijając nimi bloki kodu.

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

Przykłady

Oto jeden wzór wkładki, którego możesz użyć.

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

Możesz owinąć je w ten sposób dla wielu poleceń w skrypcie.

set -x
cmd1
cmd2
set +x

cmd3

Log4Bash

Większość ludzi jest nieświadoma, ale Bash ma również log4 *, Log4Bash . Jeśli masz bardziej skromne potrzeby, warto poświęcić czas na ich skonfigurowanie.

log4bash to próba lepszego rejestrowania skryptów Bash (tzn. ograniczenie logowania Bash do minimum).

Przykłady

Oto kilka przykładów użycia 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

Jeśli chcesz czegoś, co sklasyfikowałbym jako więcej pełnej mocy frameworka log4 *, dałbym Log4sh szansę.

fragment

log4sh został pierwotnie opracowany, aby rozwiązać problem z logowaniem, który miałem w niektórych środowiskach produkcyjnych, w których pracowałem, w których albo miałem za dużo, albo za mało. Zwłaszcza praca w firmie Cron sprawiła mi najwięcej bólu głowy dzięki ciągłym i irytującym e-mailom informującym, że wszystko działa lub że nic nie działa, ale nie szczegółowy powód. Teraz używam log4sh w środowiskach, w których logowanie ze skryptów powłoki ma kluczowe znaczenie, ale gdzie potrzebuję czegoś więcej niż zwykłego „Cześć, napraw mnie!” rodzaj wiadomości rejestrującej. Jeśli podoba Ci się to, co widzisz lub masz sugestie dotyczące ulepszeń, napisz do mnie e-maila. Jeśli zainteresowanie projektem będzie wystarczające, będę go dalej rozwijać.

log4sh został opracowany pod powłoką Bourne Again Shell (/ bin / bash) w systemie Linux, ale dołożono wszelkich starań, aby działał pod domyślną powłoką Bourne'a w systemie Solaris (/ bin / sh), ponieważ jest to podstawowa produkcja platforma używana przeze mnie.

Log4sh obsługuje kilka powłok, nie tylko Bash.

  • Bourne Shell (sh)
  • BASH - GNU Bourne Again SHell (bash)
  • DASH (myślnik)
  • Korn Shell (ksh)
  • pdksh - Public Domain Korn Shell (pdksh)

Został również przetestowany na kilku systemach operacyjnych, nie tylko na Linuksie.

  • Cygwin (pod Windows)
  • FreeBSD (obsługiwany przez użytkownika)
  • Linux (Gentoo, RedHat, Ubuntu)
  • Mac OS X
  • Solaris 8, 9, 10

Korzystanie z frameworka log4 * zajmie trochę czasu, ale warto, jeśli masz bardziej wymagające potrzeby związane z logowaniem. Log4sh korzysta z pliku konfiguracyjnego, w którym można zdefiniować programy dołączające i kontrolować formatowanie wyświetlanych danych wyjściowych.

Przykład

#! /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'

Teraz kiedy go uruchomię:

$ ./log4sh.bash 
INFO - Hello, world

UWAGA: Powyższe konfiguruje program dołączający jako część kodu. Jeśli chcesz, możesz to wyodrębnić do własnego pliku,log4sh.properties itp.

Skorzystaj z doskonałej dokumentacji Log4sh, jeśli potrzebujesz dodatkowych informacji.

slm
źródło
Dziękuję za dodane uwagi, ale głównym problemem, który mam z tym wszystkim, są wszystkie setpolecenia, które muszę wprowadzić, na przemian wokół komentarzy itp., Więc po prostu posiadanie funkcji na górze mojego skryptu, z wywołaniem funkcji pojedynczego znaku poprzedzonego wszystkie „ważne” wiersze skryptu wydawały mi się na razie ładniejsze. (pojedynczy znak, ponieważ funkcja ma nazwę jednego znaku)
Trindaz
@Trindaz - przepraszam, że jeszcze nie skończyłem odpowiedzi. Spójrz na log4bash, jeśli masz więcej potrzeb niż funkcja, którą dał terdon.
slm
1
@Trindaz - robię coś podobnego od czasu do czasu, innym podejściem, które zastosowałem, jest zawinięcie echowłasnej funkcji mecho, a następnie przekazanie przełącznika do programu zwanego -vverbose, gdy chcę wyłączyć. Mogę też sterować za pomocą 2. przełącznika argumentów, który określa nazwę funkcji, więc mam 2 osie, na których można kontrolować rejestrowanie. Jest to często brama do log4bash.
slm
1
@Trindaz set -xdrukuje polecenia podczas ich wykonywania. Nie drukuje komentarzy. set -xjest praktyczny do debugowania (w przeciwieństwie do tego, set -vco nie jest bardzo przydatne). Zsh ma lepszą wydajność set -xniż bash, na przykład pokazuje, która funkcja jest aktualnie wykonywana i numer linii źródłowej.
Gilles „SO- przestań być zły”
Dzięki @Gilles to prawda, ale dało mi to rozwinięcie wyrażeń if, co w tym przypadku było
przesadą
1

Można trap DEBUG, a następnie przetestować na BASH_COMMANDzmienną . Dodaj to na początku skryptu:

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

trap 'log "$BASH_COMMAND"' DEBUG

Kod jest czytelny; po prostu sprawdza, czy pierwszy argument zaczyna się od pythonlub pkill, i wypisuje go, jeśli tak jest. I pułapka używa BASH_COMMAND(który zawiera polecenie, które zostanie wykonane) jako pierwszego argumentu.

$ 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

Pamiętaj, że podczas casekorzystania z globów równie łatwo możesz:

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

I używaj wyrażeń regularnych.

muru
źródło
0

To jest poprawiona wersja zgrabnej funkcji Stevena Penny'ego. Drukuje argumenty w kolorze i przytacza je w razie potrzeby. Użyj go, aby selektywnie powtórzyć polecenia, które chcesz śledzić. Ponieważ cytaty są wyprowadzane, możesz kopiować wydrukowane linie i wklejać je do terminala w celu natychmiastowego ponownego wykonania podczas debugowania skryptu. Przeczytaj pierwszy komentarz, aby dowiedzieć się, co zmieniłem i dlaczego.

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
    }
  }
  ' "$@"
}

Przykładowe użycie z wyjściem:

# 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

Druga linia powyżej jest drukowana na zielono i można ją skopiować i wkleić, aby odtworzyć trzecią linię.

Dalsze uwagi

Oryginalny xc Stevena-Penny'ego jest sprytny i zasługuje na wszystkie kredyty. Zauważyłem jednak pewne problemy, ale nie mogłem bezpośrednio skomentować jego postu, ponieważ nie mam wystarczającej reputacji. Zrobiłem więc sugerowaną edycję jego postu, ale recenzenci odrzucili moją zmianę. Dlatego uciekłem się do opublikowania moich komentarzy jako tej odpowiedzi, chociaż wolałbym móc edytować własną odpowiedź Steve'a Penny'ego.

To, co zmieniłem, to odpowiedź Stevena-Penny'ego

Naprawiono: drukowanie ciągów zerowych - nie były drukowane. Naprawiono: drukowanie ciągów, które obejmują% - powodowały błędy składniowe awk. Zastąpiony for (j in ...)przez, for (j = 0, ...)ponieważ ten pierwszy nie gwarantuje kolejności przechodzenia przez tablicę (zależy od implementacji awk). Dodano 0 do liczb ósemkowych dla przenośności.

Aktualizacja

Od tego czasu Steven Penny naprawił te problemy w swojej odpowiedzi, więc te uwagi pozostały tylko dla historycznego zapisu mojej odpowiedzi. Więcej informacji znajduje się w sekcji Komentarze.

stepse
źródło
0

Możesz użyć funkcji powłoki „sh_trace” z biblioteki stdlib POSIX, aby wydrukować polecenie w kolorze przed uruchomieniem. Przykład:

zapowiedź

Podstawowa funkcja 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)
}
Steven Penny
źródło