Làm cách nào để chuyển hướng đầu ra của toàn bộ tập lệnh shell trong chính tập lệnh?


205

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

Chuyển hướng đầu ra của một lệnh đơn giản là dễ dàng, nhưng tôi muốn một cái gì đó giống 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 tập lệnh được chạy không tương tác (ví dụ: cron), hãy lưu đầu ra của mọi thứ vào một tệp. Nếu chạy tương tác từ shell, hãy để đầu ra đi ra thiết bị xuất chuẩn 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 để thấy mỗi ngày trong email, vì vậy tôi không gửi nó. Tuy nhiên, nếu một cái gì đó bên trong một kịch bản cụ thể này thất bại, điều đó quan trọng đối với tôi và tôi muốn có thể nắm bắt và gửi email đầu ra của một phần của công việc hàng ngày này.

Cập nhật: Câu trả lời của Joshua là ngay lập tức, nhưng tôi cũng muốn lưu và khôi phục thiết bị xuất chuẩn và thiết bị xuất chuẩn xung quanh toàn bộ tập lệnh, được thực hiện như sau:

# 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 có phải là tty không (test -t 0).
Chris Jester-Young

2
Nói cách khác: nếu [! -t 0]; sau đó thực hiện> somefile 2> & 1; fi
Chris Jester-Young

1
Xem ở đây để biết tất cả sự tốt lành: http://tldp.org/LDP/abs/html/io-redirection.html Về cơ bản những gì đã được Joshua nói. exec> file chuyển hướng stdout sang một tệp cụ thể, exec <file thay thế stdin bằng tệp, v.v ... Nó giống như bình thường nhưng sử dụng exec (xem man exec để biết thêm chi tiết).
Loki

Trong phần cập nhật của bạn, bạn cũng nên đóng FD 3 và 4, như vậy: exec 1>&3 2>&4 3>&- 4>&-
Gurjeet Singh

Câu trả lời:


173

Giải quyết các câu hỏi như được 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...

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

Bạn đúng rằng bạn có thể giữ nguyên thiết bị xuất chuẩn và thiết bị xuất chuẩn với các chuyển hướng bạn đã hiển thị, nhưng thường thì đơn giản hơn cho những người phải duy trì tập lệnh sau này để hiểu những gì đang diễn ra nếu bạn phạm vi mã được chuyển hướng như được hiển thị ở trên.

Các bộ phận liên quan của cuốn cẩm nang Bash là Nhóm CommandsI / O Redirection . Các bộ phận liên quan của đặc tả POSIX vỏ là Commands CompoundI / O Redirection . Bash có một số ký hiệu bổ sung, nhưng khác với đặc điểm kỹ thuật vỏ POSIX.


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.
Steve Madsen

23
Tôi đã phải làm một số việc để hiểu điều này thực sự đang làm gì, vì vậy tôi muốn chia sẻ. Các dấu ngoặc nhọn trở thành một "khối mã" , thực chất là tạo ra một hàm ẩn danh . Đầu ra mọi thứ trong khối mã sau đó 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 không khởi động một subshell , nhưng tương tự I / O ← liên kết có thể được thực hiện với subshells sử dụng dấu ngoặc đơn.
chris

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

Tốt hơn làm >>. Một số người có thói quen >. Việc đăng ký luôn an toàn và được khuyến nghị hơn so với ghi đè. Ai đó đã viết một ứng dụng sử dụng lệnh sao chép tiêu chuẩn để xuất một số dữ liệu đến cùng một đích.
neverMind9 28/12/18

Ứng dụng đó là ccc.bmw71 (.pro) (Tiện ích giám sát pin 3C, trước đây là Widget Pin Monitor Monitor) luôn xuất lịch sử pin sang /sdcard/bmw_history.txt. Nếu đã tồn tại, hãy đoán xem, QUÁ TUYỆT VỜI! Điều này làm cho tôi vô tình mất một số lịch sử pin. Tôi đặt giới hạn trong những ngày từ 30 đến một con số rất cao, điều đó đã vô hiệu hóa nó và đưa nó trở lại 30. Tôi muốn xuất lịch sử pin hiện tại trước khi nhập bản sao lưu hiện có. Sau đó, điều đó đã xảy ra.
neverMind9 28/12/18

175

Thông thường chúng ta sẽ đặt một trong số này ở hoặc gần đầu tập lệnh. Các tập lệnh phân tích các dòng lệnh của chúng sẽ thực hiện chuyển hướng sau khi phân tích cú pháp.

Gửi thiết bị xuất chuẩn vào một tập tin

exec > file

với thiết bị lỗi thời

exec > file                                                                      
exec 2>&1

nối cả stdout và stderr vào tập tin

exec >> file
exec 2>&1

Như Jonathan Leffler đã đề cập trong bình luận của mình :

execcó hai công việc riêng biệt. Đầu tiên là thay thế shell (script) hiện đang thực thi bằng một chương trình mới. Khác là thay đổi chuyển hướng I / O trong shell hiện tại. Điều này được phân biệt bằng cách không có đối số exec.


7
Tôi nói cũng thêm 2> & 1 vào cuối của điều đó, chỉ để stderr bị bắt quá. :-)
Chris Jester-Young

7
Nơi nào bạn đặt những thứ này? Ở đầu kịch bản?
colan

4
Với giải pháp này, người ta cũng phải thiết lập lại chuyển hướng thoát khỏi tập lệnh. Câu trả lời tiếp theo của Jonathan Leffler là "bằng chứng thất bại" hơn theo nghĩa này.
Chuim

6
@JohnRed: execcó hai công việc riêng biệt. Một là thay thế tập lệnh hiện tại bằng một lệnh khác, sử dụng cùng một quy trình - bạn chỉ định lệnh khác làm đối số exec(và bạn có thể điều chỉnh chuyển hướng I / O khi bạn thực hiện). Công việc khác là thay đổi chuyển hướng I / O trong tập lệnh shell hiện tại mà không thay thế nó. Ký hiệu này được phân biệt bằng cách không có lệnh làm đối số exec. Ký hiệu trong câu trả lời này là của biến thể "chỉ I / O" - nó chỉ thay đổi chuyển hướng và không thay thế tập lệnh đang chạy. ( setLệnh này tương tự đa mục đích.)
Jonathan Leffler

18
exec > >(tee -a "logs/logdata.log") 2>&1in các bản ghi trên màn hình cũng như ghi chúng vào một tệp
shriyog 2/2/2017

32

Bạn có thể làm cho toàn bộ tập lệnh có 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ó phần 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 mọi thứ vào logfile mỗi lần chạy và vẫn xuất nó ra thiết bị xuất chuẩn bằng cách thực hiện đơn giản:

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

Ý của bạn là main_factor >> /var/log/my_uber_script.log 2> & 1
Felipe Alvarez

Tôi thích sử dụng main_factor 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ả về ban đầu. Trong trường hợp bash, bạn nên thoát sau đó sử dụng 'exit $ {PIPESTATUS [0]}'.
rudimeier

7

Để lưu thiết bị xuất chuẩn và thiết bị xuất chuẩn ban đầu, 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 nhật ký ( a.txt), "walla3" sang thiết bị xuất chuẩn, "walla4" sang thiết bị lỗi chuẩn.

#!/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 khi sử dụng exec 5>&1exec 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 tránh xa nó bởi vì khi tập lệnh đượ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ả đầu ra tiêu chuẩn và lỗi tiêu chuẩn đều có thể đọc được bởi vì (đó là 'phó'?) Của một câu đố lịch sử: thiết bị đầu cuối được mở cho đọc và viết và mô tả tệp mở giống nhau được sử dụng cho cả ba mô tả tệp I / O tiêu chuẩn.
Jonathan Leffler

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.