2012-10-05 71 views
7

Tôi đã theo dõi một số câu hỏi tương tự (chẳng hạn như How to set a variable to the output from a command in Bash?), tuy nhiên các câu trả lời được chấp nhận dường như không hoạt động đối với tôi. Tôi đã không chắc chắn liệu tôi có nên làm hỏng câu hỏi của người khác hoặc đăng bản sao của riêng tôi hay không, vì vậy xin lỗi nếu tôi chọn sai ở đây.Cách lấy đầu ra của lệnh bash trong biến

Tôi muốn có được trạng thái đầu ra và thoát của một số lệnh trong một tập lệnh mà tôi đang tập hợp lại. Dưới đây là một ví dụ về những gì tôi đã được sử dụng:

cmd_output=$(rm $file) 
exit_status=$? 
if [ "${exit_status}" -eq 0 ] 
then 
log "Successfully removed the original" ${TAB_LEVEL} 
else 
fail "Failed to remove the original, the output was: \n ${cmd_output}" 
fi 

Nhật ký và thất bại chức năng bao gồm:

# Usage: fail "Failure message" 
function fail { 
echo "FATAL ERROR: $1" >> "${LOG_DIR}/${LOG_FILE}" 
exit 1 
} 

# Usage: log "Log message" 3 Where the tab-level is 3. 
function log { 
if (("${2}" > 0)) 
then 
eval "printf ' %.0s' {1..$2}" >> "${LOG_DIR}/${LOG_FILE}" 
fi 
echo "$1" >> "${LOG_DIR}/${LOG_FILE}" 
return 0 
} 

Trong ví dụ trên, chúng tôi sử dụng định dạng $ (cmd), nhưng tôi cũng đã cố gắng sử dụng backticks.

Trong log file của tôi, tất cả tôi thấy khi có một thất bại là:

FATAL ERROR: Failed to remove the original, the output was: \n

Ngoài ra, đầu ra của các lệnh thất bại kết thúc lên trên màn hình như bình thường. Có một lý do phổ biến mà các biến cmd_output của tôi sẽ còn trống không?

Trả lời

22

Bạn phải bao gồm đầu ra của dòng đầu ra sai số chuẩn đặc biệt:

cmd_output=$(rm "$file" 2>&1) 

Có ba suối mặc định trên tất cả các chương trình (được đánh số file descriptor):

0. Standard input (where the program normally reads from) 
1. Standard output (where the program normally writes to) 
2. Standard error (where the program normally writes error messages) 

Vì vậy, để nắm bắt các thông báo lỗi, chúng ta phải chuyển hướng đầu ra lỗi chuẩn (stderr) thành đầu ra tiêu chuẩn bình thường (stdout) mà sau đó sẽ được chụp bởi biểu thức $(...).

Cú pháp để chuyển hướng là thông qua toán tử ">". Ngay lập tức trước khi bạn cho biết mô tả tập tin nào để chuyển hướng (mặc định là 1, đó là stdout). Và bạn có thể chỉ định nó để chuyển hướng đến một tập tin. Nếu bạn viết dấu và (&) sau nó, bạn buộc nó chuyển hướng vào một bộ mô tả tệp khác. Do đó, trong ví dụ này, chúng ta chuyển hướng bộ mô tả tập tin 2 (stderr) vào bộ mô tả tập tin 1 (stdout).

Ngoài ra, bạn cũng có thể chuyển hướng đầu vào với một toán tử "<", nhưng trong trường hợp này, trình mô tả tệp mặc định là 0 (stdin).

Một quan sát khác là có thể thực hành tốt để đặt biến số $file giữa dấu ngoặc kép, trong trường hợp nó có ký tự khoảng trắng.

Hy vọng điều này sẽ giúp một chút =)

+1

Vui mừng khi đẩy bạn vượt mốc 1000, tiếp tục đăng! – shellter

+0

Hey Janito, Cảm ơn rất nhiều vì câu trả lời của bạn! Tôi biết về ba luồng tiêu chuẩn và các toán tử chuyển hướng, nhưng lý do duy nhất tôi đã sử dụng chúng cho đến nay là gửi mọi thứ từ một lệnh đến/dev/null hoặc tới một tệp nhật ký. Tôi thậm chí không xem xét các luồng khác nhau ở đây. Biến $ file thực sự ban đầu trong dấu ngoặc kép, tôi lấy nó ra khỏi các dấu ngoặc kép khi tôi thử nghiệm các chức năng bắt lỗi của tập lệnh của tôi, để ép buộc lỗi. Một lần nữa, cảm ơn rất nhiều! –

5

* nix lệnh thường có hai dạng đầu ra: đầu ra tiêu chuẩn (stdout) và sai số chuẩn (stderr).

FOO=$(...) chỉ chụp stdout và lá stderr không bị cản trở.

Nếu bạn muốn nội dung của stderr với cú pháp này, bạn cần phải postfix lệnh của bạn với 2>&1 để stderr được hợp nhất thành stdout. (ví dụ.như vậy: rm $file 2>&1)

+0

Hi antak, chỉ muốn nói cảm ơn một bó cho câu trả lời của bạn. Như của Janito là một chút chi tiết hơn tôi để lại nó như là câu trả lời được chấp nhận, nhưng tôi rất nhiều đánh giá cao sự giúp đỡ của bạn :). –

0

Kể từ khi chức năng fail của bạn chỉ là thoát, nó sẽ dễ dàng hơn rất nhiều để chỉ cần làm:

set -e # Abort on failure 
exec 2>> "${LOG_DIR}/${LOG_FILE}" # Append all errors to LOG_FILE 
if cmd_output=$(rm $file) 
    log "Successfully removed the original" ${TAB_LEVEL} 
fi 

Sự khác biệt duy nhất giữa này và mã gốc là nó không in văn bản FATAL ERROR:. Vì ngắn gọn là một đức tính, nên có lẽ sẽ là hoàn toàn tốt hơn nếu bỏ qua hàm log. Báo cáo lỗi lớn; thành công âm thầm.

+0

Cảm ơn William. Trong trường hợp này tôi không muốn thành công âm thầm, mặc dù nếu tôi đã viết một gói để phân phối hoặc hầu hết các mục đích khác, tôi sẽ đồng ý. Đây là phần mềm trong nhà, và cũng quan trọng như nó thực sự di chuyển các tập tin là nó giữ các chi tiết rất rõ ràng như những gì được xúc động (rất nhiều những gì đang được di chuyển là dữ liệu nhạy cảm hoặc quan trọng). Tôi cũng cần tiền tố FATAL ERROR - tệp nhật ký được chạy qua máy, dữ liệu được sao chép để tìm kiếm hoặc một vài tiền tố dòng khác. Cảm ơn bạn một lần nữa! –

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