2017-02-22 135 views
5

Tôi có một tập lệnh bash khá dài gọi ra một vài lệnh bên ngoài (git clone, wget, apt-get và các loại khác) in nhiều thứ vào đầu ra tiêu chuẩn.Làm cách nào để quản lý độ dài nhật ký bên trong tập lệnh shell?

Tôi muốn tập lệnh có một vài tùy chọn độ dài để in mọi thứ từ lệnh bên ngoài, phiên bản tóm tắt của nó (ví dụ: "Cài đặt phụ thuộc ...", "Biên dịch ...", v.v.) ở tất cả. Nhưng làm thế nào tôi có thể làm điều đó mà không làm lộn xộn tất cả các mã của tôi?

Tôi đã suy nghĩ về các giải pháp khả thi cho điều này: Một là tạo một hàm bao bọc chạy các lệnh bên ngoài và in những gì cần thiết cho đầu ra tiêu chuẩn, tùy thuộc vào các tùy chọn được đặt lúc bắt đầu. Điều này có vẻ dễ dàng hơn để thực hiện, nhưng nó có nghĩa là thêm rất nhiều lộn xộn thêm vào mã.

Giải pháp khác là gửi tất cả đầu ra tới một vài tệp bên ngoài và khi phân tích các đối số ở đầu tập lệnh, chạy tail -f trên tệp đó nếu độ dài được xác định. Điều này sẽ rất dễ thực hiện, nhưng có vẻ khá hacky với tôi và tôi lo ngại về tác động của nó.

Cái nào tốt hơn? Tôi cũng mở cửa cho các giải pháp khác.

+1

Bạn đã xem là sử dụng 'thiết -x '? Gọi lệnh này ở đầu tập lệnh của bạn và mọi lệnh sau sẽ được lặp lại. Bạn có thể tắt nó bằng 'set + x'. –

+1

Ngoài ra, bạn cũng có thể sử dụng 'set -e' để hủy bỏ tập lệnh khi bất kỳ lệnh nào thoát với trạng thái khác 0. –

+0

Không, đó không phải là những gì tôi cần. Tôi biết những lệnh nào tôi đang chạy, tôi chỉ muốn một cách để làm cho kịch bản của mình in mọi thứ mà chúng xuất ra hoặc không phụ thuộc vào một số tùy chọn. Đó là điều của người dùng nhiều hơn mã thực tế. – Ocab19

Trả lời

4

Bạn đã có ý tưởng rõ ràng nhất trong câu hỏi của mình (một hàm bao bọc), nhưng dường như bạn nghĩ nó sẽ lộn xộn. Tôi sẽ đề nghị bạn xem xét lại. Nó có thể trông giống như sau (không nhất thiết phải là một giải pháp chính thức, chỉ để cung cấp cho bạn những ý tưởng cơ bản):

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

Bạn có thể sắp xếp cho bất kỳ chuyển hướng yêu cầu (với file descriptor nếu bạn muốn) để được xử lý chức năng bao bọc, để phần còn lại của tập lệnh vẫn có thể đọc được và không có các chuyển hướng và điều kiện tùy thuộc vào mức ghi nhật ký đã chọn.

+0

Tôi vẫn không chắc liệu thêm 'nhật ký' vào mọi lệnh tôi chạy là giải pháp sạch nhất. Tôi lo lắng người khác sẽ gặp khó khăn khi đọc mã theo cách này. nhưng dường như không có cách nào tốt hơn nên tôi nghĩ tôi sẽ đi với cái này vào cuối – Ocab19

+0

@ Ocab19 - hãy xem các hàm 'log_debug_file' và' log_verbose_file' trong [answer] của tôi (http://stackoverflow.com/a/42426680/6862601). Với những điều này, chúng ta có thể loại bỏ sự mất mát/sự mất khả năng đọc được gây ra bởi một cấu trúc như "log 1" vv, tiền tố cho mọi lệnh mà chúng ta cần phải điều tiết. Nhưng chuyển hướng đầu ra và sau đó gọi chức năng đăng nhập là lộn xộn là tốt. – codeforester

1

Giải pháp 1. Cân nhắc sử dụng các bộ mô tả tệp bổ sung. Chuyển hướng mô tả tệp được yêu cầu sang STDOUT hoặc/dev/null tùy thuộc vào độ dài đã chọn. Đầu ra chuyển hướng của mỗi câu lệnh trong tập lệnh của bạn thành một bộ mô tả tệp tương ứng với tầm quan trọng của nó. Hãy xem https://unix.stackexchange.com/a/218355.

1

Giải pháp 2.

Set $ ​​required_verbosity và ống STDOUT của mọi tuyên bố trong kịch bản của bạn một kịch bản helper với hai tham số, một cái gì đó như thế này:

tuyên bố | logger actual_verbosity $ required_verbosity

Trong một tập lệnh logger echo STDIN thành STDOUT (hoặc tệp nhật ký, bất cứ điều gì) nếu $ actual_verbosity> = $ required_verbosity.

4

Cải thiện trên @ ý tưởng Fred của hơn một chút, chúng ta có thể xây dựng một thư viện khai thác gỗ nhỏ theo cách này:

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 ==="; } 

Hãy nói rằng các nguồn trên là trong một tập tin thư viện gọi logging_lib.sh, chúng ta có thể sử dụng nó trong một kịch bản shell thường xuyên theo cách này:

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

sẽ dẫn đến kết quả này:

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 

Như chúng ta có thể thấy, log_verbose không sản xuất bất kỳ sản lượng từ mức log ở DEBUG, một cấp dưới VERBOSE. Tuy nhiên, log_debug_file date.out đã sản xuất đầu ra và do đó đã làm log_execute INFO, vì cấp độ nhật ký được đặt thành DEBUG, tức là> = INFO.

Sử dụng này như là cơ sở, chúng tôi cũng có thể viết giấy gói lệnh nếu chúng ta cần điều chỉnh thậm chí tốt hơn:

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

Với những tại chỗ, các kịch bản có thể được tăng cường để có một cuộc tranh cãi --log-level level có thể xác định log verbosity nó nên chạy với.


Nếu ai là tò mò về lý do tại sao một số biến được đặt tên với một lãnh đạo nhấn mạnh trong đoạn mã trên, xem bài này:

+0

Thật tuyệt vời khi có được suy nghĩ của @ charlesduffy về điều này. – codeforester

Các vấn đề liên quan