2008-11-24 47 views
119

Có thể chuyển hướng tất cả đầu ra của một kịch bản lệnh shell Bourne đến một nơi nào đó, nhưng với các lệnh shell bên trong chính kịch bản lệnh đó?Làm cách nào để chuyển hướng đầu ra của toàn bộ tập lệnh shell trong tập lệnh?

Chuyển hướng đầu ra của một lệnh duy nhất là dễ dàng, nhưng tôi muốn một cái gì đó như thế này:

#!/bin/sh 
if [ ! -t 0 ]; then 
    # redirect all of my output to a file here 
fi 

# rest of script... 

Ý nghĩa: nếu kịch bản được điều hành không tương tác (ví dụ, cron), tiết kiệm tắt đầu ra của mọi thứ vào một tập tin. Nếu chạy tương tác từ một trình bao, hãy để đầu ra đi đến stdout như bình thường.

Tôi muốn làm điều này cho một tập lệnh thường được chạy bởi tiện ích định kỳ FreeBSD. Đó là một phần của hoạt động hàng ngày, mà tôi thường không quan tâm để xem hàng ngày trong email, vì vậy tôi không có nó gửi đi. Tuy nhiên, nếu một cái gì đó bên trong một kịch bản cụ thể này không thành công, điều đó quan trọng với tôi và tôi muốn có thể nắm bắt và gửi email kết quả đầu ra của phần này của công việc hàng ngày.

Cập nhật: Câu trả lời của Giô-suê là chỗ-on, nhưng tôi cũng muốn lưu và khôi phục stdout và stderr xung quanh toàn bộ kịch bản, được thực hiện như thế này:

# save stdout and stderr to file descriptors 3 and 4, then redirect them to "foo" 
exec 3>&1 4>&2 >foo 2>&1 

# ... 

# restore stdout and stderr 
exec 1>&3 2>&4 
+2

Kiểm tra $ TERM không phải là cách tốt nhất để kiểm tra chế độ tương tác. Thay vào đó, kiểm tra xem stdin là một tty (test -t 0). –

+2

Nói cách khác: nếu [! -t0]; sau đó exec> somefile 2> & 1; fi –

+0

Xem tại đây để biết tất cả sự tốt lành: [http://tldp.org/LDP/abs/html/io-redirection.html](http://tldp.org/LDP/abs/html/io-redirection.html Về cơ bản những gì đã được Joshua nói. exec> tập tin chuyển hướng stdout vào một tập tin cụ thể, exec Loki

Trả lời

107

Gửi stdout vào một tập tin

exec > file 

với stderr

exec > file                  
exec 2>&1 

append cả stdout và stderr nộp

exec >> file 
exec 2>&1 
+7

Tôi nói cũng thêm 2> & 1 vào cuối rằng, chỉ để stderr bị bắt quá. :-) –

+1

Huy hiệu giác ngộ cho súng nhanh nhất trong những năm trước đây. – Joshua

+0

Bạn không có ý tưởng chỉ có bao nhiêu huy hiệu giác ngộ tôi đã nhận theo cách đó. :-P –

20

Bạn có thể làm cho toàn bộ kịch bản một chức năng như thế này:

main_function() { 
    do_things_here 
} 

sau đó ở phần cuối của kịch bản có điều này:

if [ -z $TERM ]; then 
    # if not run via terminal, log everything into a log file 
    main_function 2>&1 >> /var/log/my_uber_script.log 
else 
    # run via terminal, only output to screen 
    main_function 
fi 

Ngoài ra, bạn có thể đăng nhập tất cả mọi thứ vào logfile mỗi lần chạy và vẫn xuất kết quả bằng cách thực hiện:

# log everything, but also output to stdout 
main_function 2>&1 | tee -a /var/log/my_uber_script.log 
+0

Ý của bạn là main_function >> /var/log/my_uber_script.log 2> & 1 –

+0

Tôi thích sử dụng main_function trong đường ống như vậy. Nhưng trong trường hợp này, tập lệnh của bạn không trả về giá trị trả lại ban đầu. Trong trường hợp bash bạn nên thoát rồi sử dụng 'exit $ {PIPESTATUS [0]}'. – rudimeier

87

Giải quyết câu hỏi như đã cập nhật.

#...part of script without redirection... 

{ 
    #...part of script with redirection... 
} > file1 2>file2 # ...and others as appropriate... 

#...residue of script without redirection... 

Niềng răng '{...}' cung cấp đơn vị chuyển hướng I/O. Niềng răng phải xuất hiện khi một lệnh có thể xuất hiện - đơn giản, ở đầu dòng hoặc sau dấu chấm phẩy. (Có, điều đó có thể được thực hiện chính xác hơn, nếu bạn muốn giải thích, hãy cho tôi biết.)

Bạn có quyền giữ nguyên stdout và stderr với các chuyển hướng bạn đã cho thấy, nhưng nó thường đơn giản hơn cho những người phải duy trì kịch bản sau để hiểu điều gì đang xảy ra nếu bạn phạm vi mã được chuyển hướng như được hiển thị ở trên.

+3

Điều này rõ ràng hơn nhiều so với việc lưu các mô tả ban đầu và khôi phục chúng sau này. –

+13

Tôi đã phải làm một số googling để hiểu những gì điều này thực sự làm, vì vậy tôi muốn chia sẻ. Các dấu ngoặc nhọn trở thành ["khối mã"] (http://www.tldp.org/LDP/abs/html/special-chars.html#CODEBLOCKREF), trong đó, có hiệu lực, tạo ra một hàm _anonymous_. Tất cả mọi thứ trong khối mã có thể được chuyển hướng (Xem ** Ví dụ 3-2 ** từ liên kết đó). Cũng lưu ý rằng dấu ngoặc nhọn _do not_ khởi chạy [subshell] (http://www.tldp.org/LDP/abs/html/subshells.html), nhưng tương tự [chuyển hướng I/O] (http: // www .tldp.org/LDP/abs/html/ioredirintro.html) _can_ được thực hiện với subshells bằng cách sử dụng dấu ngoặc đơn. – chris

+1

Tôi thích giải pháp này tốt hơn so với các giải pháp khác.Ngay cả một người chỉ hiểu biết cơ bản nhất về chuyển hướng I/O cũng có thể hiểu điều gì đang xảy ra. Thêm vào đó, nó tiết hơn. Và, với tư cách là một người Python, tôi yêu thích tiết tấu. –

2
[ -t <&0 ] || exec >> test.log 
2

Đối với lưu stdout gốc và thiết bị lỗi chuẩn bạn có thể sử dụng:

exec [fd number]<&1 
exec [fd number]<&2 

Ví dụ, đoạn mã sau sẽ in "walla1" và "walla2" vào tập tin log (a.txt), "walla3 "để stdout," walla4 "để stderr.

#!/bin/bash 

exec 5<&1 
exec 6<&2 

exec 1> ~/a.txt 2>&1 

echo "walla1" 
echo "walla2" >&2 
echo "walla3" >&5 
echo "walla4" >&6 
+1

Thông thường, sẽ tốt hơn nếu sử dụng 'exec 5> & 1' và' exec 6> & 2', sử dụng ký hiệu chuyển hướng đầu ra thay vì ký hiệu chuyển hướng đầu vào cho đầu ra. Bạn nhận được ngay với nó bởi vì khi kịch bản được chạy từ một thiết bị đầu cuối, đầu vào tiêu chuẩn cũng có thể ghi được và cả hai tiêu chuẩn đầu ra và lỗi tiêu chuẩn có thể đọc được bởi đức hạnh (hoặc là 'phó'?) Của một quirk lịch sử: thiết bị đầu cuối được mở ra cho đọc và viết và cùng một [mô tả tệp mở] (http://pubs.opengroup.org/onlinepubs/9699919799/functions/open.html) được sử dụng cho cả ba mô tả tệp I/O chuẩn. –

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