Chuyển hướng tất cả các lệnh stderr tiếp theo bằng exec


43

Tôi có một tệp bash mà tôi cần chuyển hướng tất cả đầu ra sang một tệp, nhật ký gỡ lỗi cũng như đến thiết bị đầu cuối. Tôi cần chuyển hướng cả stdout và stderr sang gỡ lỗi và đăng nhập nó cho tất cả các lệnh trong tập lệnh.

Tôi không muốn thêm 2>&1 | tee -a $DEBUGcho mỗi lệnh trong tệp. Tôi có thể sống với | tee -a $DEBUG.

Tôi nhớ có một cách để làm điều đó với một cái gì đó như exec 2>&1.

Hiện tại tôi đang sử dụng một cái gì đó như sau:

#!/bin/bash
DEBUGLOG=/tmp/debug
exec 2>&1
somecommand | tee -a $DEBUGLOG
somecommand2 | tee -a $DEBUGLOG
somecommand3 | tee -a $DEBUGLOG

Nhưng nó không hoạt động. Có ai có một giải pháp / có thể giải thích nguyên nhân?


1
Trong một số shell, |&hoạt động như một phím tắt cho 2>&1 |, nó ít nhất thuận tiện hơn một chút.
Kevin

Câu trả lời:


39

Đối với một giải pháp để chuyển hướng nhiều lệnh cùng một lúc:

#!/bin/bash
{
    somecommand 
    somecommand2
    somecommand3
} 2>&1 | tee -a $DEBUGLOG

Tại sao giải pháp ban đầu của bạn không hoạt động: exec 2> & 1 sẽ chuyển hướng đầu ra lỗi tiêu chuẩn sang đầu ra tiêu chuẩn của shell của bạn, nếu bạn chạy tập lệnh của mình từ bàn điều khiển, sẽ là bàn điều khiển của bạn. chuyển hướng ống trên các lệnh sẽ chỉ chuyển hướng đầu ra tiêu chuẩn của lệnh.

Theo quan điểm của somecommandnó, đầu ra tiêu chuẩn của nó đi vào một đường ống được kết nối teevà lỗi tiêu chuẩn đi vào cùng một tệp / pseudofile như lỗi tiêu chuẩn của vỏ, mà bạn chuyển hướng đến đầu ra tiêu chuẩn của vỏ, đó sẽ là bàn điều khiển nếu bạn chạy chương trình của bạn từ bàn điều khiển.

Cách duy nhất để giải thích là xem điều gì thực sự xảy ra:

Môi trường ban đầu của shell của bạn có thể trông như thế này nếu bạn chạy nó từ thiết bị đầu cuối:

stdin -> /dev/pts/42
stdout -> /dev/pts/42
stderr -> /dev/pts/42

Sau khi bạn chuyển hướng lỗi tiêu chuẩn thành đầu ra tiêu chuẩn ( exec 2>&1), về cơ bản, bạn ... không thay đổi gì. Nhưng nếu bạn chuyển hướng đầu ra tiêu chuẩn của tập lệnh thành một tệp, bạn sẽ kết thúc với một môi trường như thế này:

stdin -> /dev/pts/42
stdout -> /your/file
stderr -> /dev/pts/42

Sau đó, chuyển hướng lỗi tiêu chuẩn shell thành đầu ra tiêu chuẩn sẽ kết thúc như thế này:

stdin -> /dev/pts/42
stdout -> /your/file
stderr -> /your/file

Chạy một lệnh sẽ kế thừa môi trường này. Nếu bạn chạy một lệnh và chuyển nó sang tee, môi trường của lệnh sẽ là:

stdin -> /dev/pts/42
stdout -> pipe:[4242]
stderr -> /your/file

Vì vậy, lỗi tiêu chuẩn của lệnh của bạn vẫn đi vào cái mà shell sử dụng làm lỗi tiêu chuẩn của nó.

Bạn thực sự có thể thấy môi trường của một lệnh bằng cách nhìn vào /proc/[pid]/fd: sử dụng ls -lđể liệt kê nội dung của liên kết tượng trưng. Các 0tập tin ở đây là đầu vào tiêu chuẩn, 1là đầu ra tiêu chuẩn và 2là lỗi tiêu chuẩn. Nếu lệnh mở thêm tệp (và hầu hết các chương trình làm), bạn cũng sẽ thấy chúng. Một chương trình cũng có thể chọn chuyển hướng hoặc đóng đầu vào / đầu ra tiêu chuẩn và tái sử dụng 0, 12.


41

Bạn có thể sử dụng exec như thế này ở đầu tập lệnh của bạn:

exec > >(tee "$HOME/somefile.log") 2>&1

Ví dụ:

#!/bin/bash -

exec > >(tee "$HOME/somefile.log") 2>&1

echo "$HOME"
echo hi
date
date +%F
echo bye 1>&2

Cung cấp cho tôi đầu ra cho tệp $HOME/somefile.logvà đến thiết bị đầu cuối như thế này:

/home/saml
hi
Sun Jan 20 13:54:17 EST 2013
2013-01-20
bye

2
Lưu ý rằng điều này sử dụng bashism - nó có thể không hoạt động trong các shell khác (ví dụ: dấu gạch ngang). Nhưng vì câu hỏi được chỉ định bash, +1.
Richard Hansen

8
@RichardHansen, thay thế quá trình là một tính năng được giới thiệu bởi ksh, không phải bash và cũng được hỗ trợ bởi zsh vì vậy tôi sẽ không gọi nó là bashism .
Stéphane Chazelas

6
@StephaneChazelas: Bạn làm cho một điểm tốt. Tôi chỉ muốn chỉ ra rằng cú pháp không được hỗ trợ bởi tiêu chuẩn POSIX và do đó sẽ không hoạt động phổ biến trong /bin/shcác tập lệnh (nhiều người sử dụng sai cú pháp bash trong /bin/shcác tập lệnh).
Richard Hansen

Đối với tôi điều này cho /dev/fd/62: Operation not supportedbất kỳ manh mối?
Eun

1
Có cách nào để không chuyển hướng stderr ngoại trừ tệp nhật ký không? Nếu kịch bản gốc là myscriptvà tôi chạy ./myscript > /dev/null, tôi vẫn sẽ thấy byenó đến từ đâu echo bye >&2.
Martin Jambon

0

Viết stderr và stdout vào một tệp, hiển thị stderr trên màn hình (trên thiết bị xuất chuẩn)

exec 2> >(tee -a -i "$HOME/somefile.log")
exec >> "$HOME/somefile.log"

Hữu ích cho crons, vì vậy bạn có thể nhận được lỗi (và chỉ lỗi) qua thư

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.