Bagaimana saya bisa mencetak hanya perintah tertentu dari skrip bash saat dijalankan?

19

Saya memiliki skrip bash dengan berbagai pernyataan if berdasarkan argumen baris perintah yang saya berikan ketika memanggilnya. Memiliki semacam keluaran untuk perintah apa yang sedang dijalankan sangat membantu untuk mengkonfirmasi aliran melalui semua pernyataan if, tetapi solusi saya saat ini memberi saya terlalu banyak informasi.

Menggunakan set -vdalam skrip agak membantu untuk melihat perintah yang dicetak ke layar saat dijalankan di skrip, namun saya mendapatkan terlalu banyak perintah. Hampir seperti seluruh salinan naskah.

Saya ingin output yang menunjukkan perintah apa yang sedang dijalankan, tetapi saya tidak ingin melihat komentar, baris baru, ekspresi dalam pernyataan if, dll.

Apakah ada cara saya bisa melewatkan semua kemungkinan output yang dihasilkan oleh opsi -v melalui regex terlebih dahulu sebelum dicetak? Atau beberapa solusi lain untuk mendapatkan bash hanya dengan perintah output dari "tipe" tertentu (mis. Yang menggunakan executable dan bukan hanya bash pernyataan spesifik, komentar dll?)

[1] /programming/257616/sudo-changes-path-why sangat membantu dalam hal ini dan saya mendapat saran untuk set -vpenggunaannya.

Edit :

Skrip yang mirip (tetapi tidak identik) dengan skrip yang saya jalankan:

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

Saya hanya ingin 2 set jalur output dari skrip ini. Pertama:

python ascript.py

Kedua:

pkill -f nginx
nginx -c /some/current/directory/conf/artfndr_nginx.conf
Trindaz
sumber
1
Tentu saja Anda dapat menguraikannya tetapi kami tidak dapat membantu kecuali jika Anda menunjukkan kepada kami skrip dan menjelaskan bagian mana dari set -voutput yang Anda inginkan dan mana yang tidak.
terdon

Jawaban:

12

Ketika saya menulis skrip bash yang lebih kompleks, saya menggunakan sedikit fungsi untuk menjalankan perintah yang juga akan mencetak perintah yang dijalankan ke file log:

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

Kemudian, dalam skrip saya, saya menjalankan perintah seperti ini:

runthis "cp /foo/bar /baz/"

Jika Anda tidak ingin perintah dicetak, jalankan saja secara normal.

Anda dapat mengatur $LOGke nama file atau hanya menghapusnya dan mencetak ke stdout atau stderr.

terdon
sumber
+1 Saya juga dapat menjalankan ini di dalam skrip saya dengan hanya menambahkan perintah "penting" dengan versi nama fungsi yang pendek, sehingga garis-garisnya terlihat seperti v python ascript.pytanpa harus menyertakan dalam kutipan dan kehilangan penyorotan kode vim
Trindaz
@ Trindaz tanda kutip ada ketika Anda perlu melewati variabel dalam perintah Anda, jika variabel berisi spasi Anda mungkin memiliki masalah sebaliknya.
terdon
eval ..... || ok=1: akan ditetapkan ok ke "1" hanya ketika "eval ..." gagal ?? Mungkin maksudmu "&&"? Dan jika Anda maksudkan itu, tambahkan "ok = 0" sebelum baris eval, jadi "reset" setiap kali. Atau cukup ganti nama "ok" menjadi "error"? sepertinya itulah yang dimaksud di sini. Jadi pada akhirnya:eval "$@" 2>> "$LOG" && error=0 || error=1
Olivier Dulac
@OlivierDulac, dalam versi yang saya gunakan ini, saya memiliki okvariabel yang akan menghentikan skrip jika ada perintah yang gagal. Karena itu tidak relevan di sini, saya menghapusnya tetapi lupa menghapus || ok=1. Terima kasih, perbaiki sekarang.
terdon
Solusi yang sangat baik! Saya harus menghapus "pernyataan eval yang mengelilinginya, karena perintah sudah dikelilingi oleh "s
gromit190
11

Gunakan sub-shell, yaitu:

( set -x; cmd1; cmd2 )

Sebagai contoh:

( set -x; echo "hi there" )

cetakan

+ echo 'hi there'
hi there
Nik
sumber
Saya lebih suka yang ini set -x; cmd; set +xkarena beberapa alasan. Pertama, itu tidak me-reset set -xkalau-kalau sudah hidup sebelumnya. Kedua, penghentian skrip di dalamnya tidak menyebabkan jebakan dieksekusi dengan pengaturan verbose aktif.
Oliver Gondža
2

Saya telah melihat metode yang digunakan mirip dengan @ terdon. Ini adalah awal dari apa yang disebut oleh bahasa pemrograman tingkat tinggi logger, dan menawarkan pustaka yang ditiup penuh, seperti log4J (Java), log4Perl (Perl) dll.

Anda bisa mendapatkan sesuatu yang serupa menggunakan set -xdi Bash seperti yang telah Anda sebutkan tetapi Anda dapat menggunakannya untuk mengaktifkan debugging hanya sebagian dari perintah dengan membungkus blok kode dengan mereka seperti itu.

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

Contohnya

Berikut adalah pola satu garis yang dapat Anda gunakan.

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

Anda dapat membungkusnya seperti ini untuk beberapa perintah dalam skrip.

set -x
cmd1
cmd2
set +x

cmd3

Log4Bash

Kebanyakan orang tidak menyadari tetapi Bash juga memiliki log4 * juga, Log4Bash . Jika Anda memiliki kebutuhan yang lebih sederhana, ini mungkin sepadan dengan waktu untuk mengaturnya.

log4bash adalah upaya untuk memiliki pendataan yang lebih baik untuk skrip Bash (yaitu membuat penebangan di Bash kurang menyedot).

Contohnya

Berikut adalah beberapa contoh penggunaan 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

Jika Anda menginginkan apa yang akan saya klasifikasikan sebagai kekuatan penuh kerangka log4 * maka saya akan mencoba Log4sh .

kutipan

log4sh pada awalnya dikembangkan untuk memecahkan masalah logging yang saya miliki di beberapa lingkungan produksi yang saya telah bekerja di mana saya memiliki terlalu banyak logging, atau tidak cukup. Pekerjaan Cron khususnya membuat saya sangat sakit kepala dengan email mereka yang konstan dan menjengkelkan memberi tahu saya bahwa semuanya bekerja, atau tidak ada yang berhasil tetapi tidak ada alasan terperinci mengapa. Saya sekarang menggunakan log4sh di lingkungan di mana penebangan dari skrip shell sangat penting, tetapi di mana saya membutuhkan lebih dari sekadar "Halo, perbaiki saya!" jenis pesan logging. Jika Anda menyukai apa yang Anda lihat, atau memiliki saran untuk perbaikan, silakan kirimkan saya email. Jika ada cukup minat dalam proyek ini, saya akan mengembangkannya lebih lanjut.

log4sh telah dikembangkan di bawah Bourne Again Shell (/ bin / bash) di Linux, tetapi sangat hati-hati telah diambil untuk memastikan itu bekerja di bawah Bourne Shell default Solaris (/ bin / sh) karena ini merupakan produksi utama platform yang digunakan oleh saya sendiri.

Log4sh mendukung beberapa shell, bukan hanya Bash.

  • Bourne Shell (sh)
  • BASH - GNU Bourne Again SHell (bash)
  • DASH (tanda hubung)
  • Korn Shell (ksh)
  • pdksh - Public Domain Korn Shell (pdksh)

Ini juga telah diuji pada beberapa OS, bukan hanya Linux.

  • Cygwin (di bawah Windows)
  • FreeBSD (didukung pengguna)
  • Linux (Gentoo, RedHat, Ubuntu)
  • Mac OS X
  • Solaris 8, 9, 10

Menggunakan kerangka kerja log4 * akan membutuhkan waktu untuk mempelajarinya, tetapi itu sangat berharga jika Anda memiliki lebih banyak kebutuhan dari pencatatan Anda. Log4sh menggunakan file konfigurasi tempat Anda dapat mendefinisikan appenders dan mengontrol format untuk output yang akan muncul.

Contoh

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

Sekarang ketika saya menjalankannya:

$ ./log4sh.bash 
INFO - Hello, world

CATATAN: Di atas mengkonfigurasi appender sebagai bagian dari kode. Jika Anda suka ini dapat diekstraksi ke file sendiri, log4sh.propertiesdll.

Konsultasikan dokumentasi yang sangat baik untuk Log4sh jika Anda memerlukan detail lebih lanjut

slm
sumber
Terima kasih atas catatan yang ditambahkan, tetapi masalah utama yang saya miliki dengan itu adalah semua setperintah yang saya perlu perkenalkan, bergantian di sekitar komentar dll, jadi hanya memiliki fungsi di bagian atas skrip saya, dengan panggilan fungsi karakter tunggal yang ditambahkan ke semua baris "penting" dalam skrip tampak lebih rapi bagi saya untuk saat ini. (karakter tunggal karena fungsinya memiliki nama karakter tunggal)
Trindaz
@ Trindaz - maaf saya belum menyelesaikan jawaban saya. Lihatlah log4bash jika Anda memiliki lebih banyak kebutuhan yang diberikan fungsi yang terdon.
slm
1
@ Trindaz - Saya melakukan sesuatu yang serupa dari waktu ke waktu, pendekatan lain yang saya gunakan adalah membungkus echofungsi saya sendiri mecho,, dan kemudian beralih ke program yang disebut -vverbose ketika saya ingin mematikan semuanya. Saya juga bisa mengendalikannya dengan saklar argumen ke-2 yang menentukan nama fungsi, jadi saya punya 2 sumbu untuk mengontrol pencatatan. Ini sering merupakan gateway untuk menginginkan log4bash.
slm
1
@Trindaz set -xmencetak perintah saat dijalankan. Itu tidak mencetak komentar. set -xpraktis untuk debugging (tidak seperti set -vyang tidak terlalu berguna). Zsh memiliki output yang lebih baik set -xdaripada bash, misalnya menunjukkan fungsi yang saat ini sedang dieksekusi dan nomor baris sumber.
Gilles 'SANGAT berhenti menjadi jahat'
Terima kasih @Gilles itu benar, tetapi itu memberi saya ekspansi ekspresi if, yang berlebihan dalam hal ini
Trindaz
1

Anda bisa trap DEBUGdan kemudian menguji yang BASH_COMMANDvariabel . Tambahkan ini ke bagian atas skrip:

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

trap 'log "$BASH_COMMAND"' DEBUG

Kode dapat dibaca; itu hanya menguji apakah argumen pertama dimulai dengan pythonatau pkill, dan mencetaknya jika itu masalahnya. Dan trap menggunakan BASH_COMMAND(yang berisi perintah yang akan dieksekusi) sebagai argumen pertama.

$ 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

Perhatikan bahwa saat casemenggunakan gumpalan, Anda bisa dengan mudah melakukannya:

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

Dan gunakan ekspresi reguler.

muru
sumber
0

Ini adalah versi revisi dari fungsi rapi Steven Penny. Ini mencetak argumen dalam warna dan mengutipnya sesuai kebutuhan. Gunakan untuk memilih perintah yang ingin Anda lacak secara selektif. Karena kutipan adalah output, Anda dapat menyalin garis yang dicetak dan menempelnya ke terminal untuk segera dieksekusi kembali saat Anda sedang men-debug sebuah skrip. Baca komentar pertama untuk mengetahui apa yang saya ubah dan mengapa.

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

Contoh penggunaan dengan output:

# 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

Baris kedua di atas mencetak dalam warna hijau dan dapat disalin-salin untuk mereproduksi baris ketiga.

Keterangan Lebih Lanjut

@ Steven-Penny xc asli itu cerdas dan dia layak mendapatkan semua pujian untuk itu Namun, saya melihat beberapa masalah, tetapi saya tidak dapat mengomentari posnya secara langsung karena saya tidak memiliki reputasi yang cukup. Jadi saya membuat edit yang disarankan untuk posnya tetapi pengulas menolak suntingan saya. Karena itu saya memilih untuk memposting komentar saya sebagai jawaban ini, meskipun saya lebih suka untuk dapat mengedit jawaban Steve Penny sendiri.

Apa yang saya ubah adalah jawaban Steven-Penny

Diperbaiki: mencetak string null - tidak dicetak. Tetap: mencetak string yang menyertakan %- mereka menyebabkan kesalahan sintaks awk. Diganti for (j in ...)dengan for (j = 0, ...)karena yang pertama tidak menjamin urutan array traversal (tergantung awk implementasi). Menambahkan 0 ke angka oktal untuk portabilitas.

Memperbarui

Steven Penny sejak itu memperbaiki masalah-masalah itu dalam jawabannya, jadi komentar-komentar ini tetap hanya untuk catatan sejarah jawaban saya. Lihat bagian Komentar untuk perincian lebih lanjut.

stepse
sumber
0

Anda dapat menggunakan fungsi shell "sh_trace" dari perpustakaan stdlib POSIX untuk mencetak perintah dalam warna sebelum menjalankannya. Contoh:

pratinjau

Fungsi Awk yang mendasarinya:

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
sumber