Bir bash betiğinden çalıştırıldıklarında yalnızca belirli komutları nasıl yazdırabilirim?


19

Ben bash komut dosyasında çağırırken iletmek komut satırı bağımsız değişkenleri dayalı çeşitli if ifadeleri var. Hangi komutların çalıştırıldığına ilişkin bir tür çıktıya sahip olmak, if ifadelerinin hepsinden akışı doğrulamak için yararlıdır, ancak mevcut çözümüm bana çok fazla bilgi veriyor .

set -vKomut dosyasında kullanmak , komut dosyasında çalıştırıldıkça ekrana yazdırılan komutları görmek için biraz yardımcı oldu, ancak çok fazla komut alıyorum . Neredeyse senaryonun bütün bir kopyası gibi.

Hangi komutların çalıştırıldığını gösteren çıktılar istiyorum, ancak yorumları, yeni satırları, if ifadelerinde ifadeleri görmek istemiyorum.

-V seçeneği tarafından oluşturulan tüm olası çıktıları yazdırılmadan önce bir normal ifade aracılığıyla geçirebilmemin bir yolu var mı? Veya bash'ı yalnızca belirli bir "tür" komutunun çıktısını almak için başka bir çözüm (örneğin, sadece bash belirli ifadeleri, yorumları vb. Değil çalıştırılabilir dosyaları kullanan?)

[1] /programming/257616/sudo-changes-path-why bu konuda oldukça yardımcı oldu ve set -vkullanım için öneri aldım .

Düzenle :

Çalıştığımın komut dosyasına benzer (fakat aynı değil):

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

Bu komut dosyasından çıkış satırlarının yalnızca 2 olası kümesini istiyorum. İlk:

python ascript.py

İkinci:

pkill -f nginx
nginx -c /some/current/directory/conf/artfndr_nginx.conf

1
Elbette ayrıştırabilirsiniz, ancak senaryoyu bize göstermez ve set -vçıktının hangi kısımlarını istediğinizi ve hangilerini yapmadığınızı açıklamazsanız yardımcı olamayız .
terdon

Yanıtlar:


12

Daha karmaşık bash komut dosyaları yazdığımda, komutları çalıştırmak için de bir günlük dosyasına çalıştırılan komutları yazdıracak küçük bir işlev kullanıyorum:

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

Sonra, benim komut dosyası, böyle komutları çalıştırın:

runthis "cp /foo/bar /baz/"

Bir komutun yazdırılmasını istemiyorsanız, normal şekilde çalıştırın.

$LOGDosya adını bir dosya olarak ayarlayabilir veya kaldırabilir ve stdout veya stderr'a yazdırabilirsiniz.


+1 Ayrıca, fonksiyonun kısa adlandırılmış bir versiyonuyla "önemli" komutları önceden yazarak bunu senaryomun içinde çalıştırabildim, bu yüzden satırlar v python ascript.pytırnak içine almak ve vim kod vurgulamamı kaybetmek zorunda kalmadan bir şeye benziyor
Trindaz

@Trindaz, komutlarınızda değişkenleri geçirmeniz gerektiğinde tırnak işaretleri vardır; değişkenler boşluk içeriyorsa, aksi takdirde sorun yaşayabilirsiniz.
terdon

eval ..... || ok=1: sadece "eval ..." başarısız olduğunda Tamam "1" olarak ayarlanır? Belki "&&" demek istediniz? Bunu demek istediyseniz, değerlendirme satırının önüne bir "ok = 0" ekleyin, bu yüzden her seferinde "sıfırla". Veya basitçe "tamam" ı "hata" olarak yeniden adlandırmak? burada kastedilen şey bu. Sonunda:eval "$@" 2>> "$LOG" && error=0 || error=1
Olivier Dulac

@OlivierDulac, kullandığım sürümünde, okherhangi bir komut başarısız olursa komut dosyasını durduracak bir değişkenim var. Burada alakalı olmadığı için kaldırdım ama silmeyi unuttum || ok=1. Teşekkürler, düzeltildi.
terdon

Mükemmel çözüm! Ben kaldırmak zorunda "komut zaten çevrilidir çünkü çevredeki eval deyimi "s
gromit190

11

Bir alt kabuk kullanın, yani:

( set -x; cmd1; cmd2 )

Örneğin:

( set -x; echo "hi there" )

baskılar

+ echo 'hi there'
hi there

Bunu set -x; cmd; set +xbirkaç nedenden dolayı tercih ederim . İlk set -xolarak, daha önce açık olması durumunda sıfırlanmaz . İkincisi, içindeki betiğin sonlandırılması tuzakların ayrıntılı ayarlarla yürütülmesine neden olmaz.
Oliver Gondža

2

@ Terdon'unkine benzer yöntemler kullandım. Daha yüksek seviyeli programlama dillerinin logger olarak adlandırdığı ve log4J (Java), log4Perl (Perl) gibi tam kütüphaneler olarak sundukları şeylerin başlangıcıdır.

set -xBash'te bahsettiğiniz gibi kullanarak benzer bir şey elde edebilirsiniz, ancak kod bloklarını onlarla bu şekilde sararak hata ayıklamayı sadece bir komut alt kümesini açmak için kullanabilirsiniz.

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

Örnekler

İşte kullanabileceğiniz bir astar deseni.

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

Bir komut dosyasında birden çok komut için bunları bu şekilde sarabilirsiniz.

set -x
cmd1
cmd2
set +x

cmd3

Log4Bash

Çoğu insan bilmez ama Bash'in log4 * özelliği de vardır, Log4Bash . Daha mütevazı ihtiyaçlarınız varsa, bunu ayarlamak için zaman ayırmaya değer olabilir.

log4bash, Bash betikleri için daha iyi günlüğe kaydetme girişimidir (yani Bash'te günlüğe kaydetmeyi daha az emer).

Örnekler

Log4bash kullanımına bazı örnekler.

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

Eğer bir log4 * çerçevesinin tüm gücü olarak sınıflandırmak istiyorum ne istiyorsanız o zaman Log4sh denemek istiyorum .

alıntı

log4sh başlangıçta çok fazla günlük kaydına sahip olduğum veya yeterli olmadığım yerlerde çalıştığım bazı üretim ortamlarında yaşadığım bir günlük kaydı sorununu çözmek için geliştirildi. Özellikle Cron işleri bana her şeyin işe yaradığını ya da hiçbir şeyin işe yaramadığını, ancak ayrıntılı bir neden olmadığını söyleyen sürekli ve sinir bozucu e-postalarıyla en çok baş ağrısına neden oldu. Artık kabuk komut dosyalarından günlüğün kritik olduğu ortamlarda log4sh kullanıyorum, ancak basit bir "Merhaba, beni düzelt!" günlük iletisinin türü. Gördüklerinizi beğendiyseniz veya iyileştirmeler hakkında herhangi bir öneriniz varsa, lütfen bana bir e-posta bırakmaktan çekinmeyin. Projeye yeterince ilgi varsa, onu daha da geliştireceğim.

log4sh Linux'ta Bourne Again Shell (/ bin / bash) altında geliştirilmiştir, ancak birincil üretim olması nedeniyle varsayılan Bourne Solaris Kabuğu (/ bin / sh) altında çalıştığından emin olmak için büyük özen gösterilmiştir. kendim kullandım.

Log4sh, sadece Bash'ı değil, birkaç mermiyi destekler.

  • Bourne Kabuğu (sh)
  • BASH - GNU Bourne Yine Kabuğu (bash)
  • DASH (tire)
  • Korn Kabuğu (ksh)
  • pdksh - Kamusal Alan Korn Kabuğu (pdksh)

Ayrıca sadece Linux üzerinde değil, birçok işletim sisteminde test edilmiştir.

  • Cygwin (Windows altında)
  • FreeBSD (kullanıcı destekli)
  • Linux (Gentoo, RedHat, Ubuntu)
  • Mac OS X
  • Solaris 8, 9, 10

Bir log4 * çerçevesi kullanmanın öğrenilmesi biraz zaman alacaktır, ancak günlüğünüzden daha zorlu ihtiyaçlarınız varsa buna değer. Log4sh, ekleri tanımlayabileceğiniz ve çıkacak çıktı için biçimlendirmeyi kontrol edebileceğiniz bir yapılandırma dosyası kullanır.

Misal

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

Şimdi çalıştırdığımda:

$ ./log4sh.bash 
INFO - Hello, world

Not: Yukarıdaki ek kodun bir parçası olarak yapılandırır. İsterseniz kendi dosyasına log4sh.propertiesvb. Çıkarılabilir .

Daha fazla ayrıntıya ihtiyacınız varsa Log4sh için mükemmel belgelere bakın.


Eklenen notlar için teşekkürler, ama ana sorun ben settanıtmak gerekir tüm komutları, yorumları vb etrafında dönüşümlü, bu yüzden sadece benim komutumun üstünde bir fonksiyona sahip tek bir karakter fonksiyon çağrısı ile ekli Senaryodaki tüm "önemli" satırlar şimdilik benim için daha temiz görünüyordu. (işlevin tek bir karakter adı olduğu için tek karakter)
Trindaz

@Trindaz - Üzgünüm cevabımı henüz bitirmemiştim. Terdon'un verdiği işleve daha fazla ihtiyacınız varsa log4bash'e bir göz atın.
slm

1
@Trindaz - Zaman zaman benzer bir şey yapıyorum, kullandığım diğer yaklaşım echokendi işlevimi sarmak mechove daha sonra bir -vşeyleri kapatmak istediğimde ayrıntılı olarak adlandırılan programa geçmek . Ben de fonksiyonun adını belirten bir 2. argüman anahtarı ile kontrol edebilirsiniz, bu yüzden günlüğü kontrol etmek için 2 ekseni var. Bu genellikle log4bash istemenin kapısıdır.
slm

1
@Trindaz set -xkomutları yürütüldükçe yazdırır. Yorum yazdırmaz. set -xhata ayıklama için pratiktir (aksine set -vçok yararlı değildir). Zsh, set -xbash için daha iyi bir çıkışa sahiptir , örneğin, şu anda hangi işlevin yürütülmekte olduğunu ve kaynak satır numarasını gösterir.
Gilles 'SO- kötü olmayı bırak'

Teşekkürler @Gilles bu doğru, ama bana bu durumda aşırıya
kaçan

1

Değişkenitrap DEBUG test edebilir ve test edebilirsinizBASH_COMMAND . Bunu betiğin en üstüne ekleyin:

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

trap 'log "$BASH_COMMAND"' DEBUG

Kod okunabilir; sadece ilk argümanın pythonveya ile başlayıp başlamadığını test eder pkillve durum böyleyse basar. Ve tuzak BASH_COMMANDilk argüman olarak (çalıştırılacak komutu içeren) kullanır .

$ 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

Not ederken o casekullandığı Neználkovo, tıpkı kolayca yapabileceği:

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

Ve düzenli ifadeler kullanın.


0

Bu Steven Penny'nin düzgün işlevinin gözden geçirilmiş bir versiyonudur. Argümanlarını renkli yazdırır ve gerektiğinde tırnak içine alır. İzlemek istediğiniz komutları seçici olarak yankılamak için kullanın. Tırnak işaretleri çıktığı için, bir komut dosyasında hata ayıklarken basılı satırları kopyalayıp terminale hemen yeniden yapıştırabilirsiniz. Neyi değiştirdiğimi ve nedenini öğrenmek için ilk yorumu okuyun.

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

Çıktı ile örnek kullanım:

# 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

Yukarıdaki ikinci satır yeşil renkte yazdırılır ve üçüncü satırı yeniden oluşturmak için kopyala yapıştırılabilir.

Diğer Açıklamalar

@ Steven-Penny'nin orijinal xc zeki ve onun için tüm kredileri hak ediyor. Ancak, bazı sorunları fark ettim, ancak yeterli bir itibara sahip olmadığım için doğrudan yayınına yorum yapamadım. Bu yüzden görevinde önerilen bir düzenleme yaptım ancak yorumcular düzenlememi reddetti. Bu nedenle, yorumlarımı bu cevap olarak göndermeye başvurdum, ancak Steve Penny'nin kendi cevabını düzenlemeyi tercih ederdim.

Steven-Penny'nin cevabı ile değiştirdiklerim

Düzeltildi: boş dizeler yazdırılıyor - yazdırılmıyorlardı. Düzeltildi: %Aşağıdakileri içeren yazdırma dizeleri - awk sözdizimi hatalarına neden oldular. Değiştirilen for (j in ...)ile for (j = 0, ...)eski dizi kastetmek ( 's awk uygulaması bağımlı) sırasını garanti etmez çünkü. Taşınabilirlik için sekizlik sayılara 0 eklendi.

Güncelleme

Steven Penny's o zamandan beri bu sorunları cevabında düzeltti, bu yüzden bu açıklamalar sadece cevabımın tarihsel kaydı için kalıyor. Daha fazla ayrıntı için Yorumlar bölümüne bakın.


0

Komutu çalıştırmadan önce renkli yazdırmak için POSIX stdlib kitaplığındaki "sh_trace" kabuk işlevini kullanabilirsiniz . Misal:

Ön izleme

Temel Awk işlevi:

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)
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.