Làm cách nào để chuyển hướng thiết bị xuất chuẩn sang tệp và thiết bị xuất chuẩn + thiết bị xuất chuẩn sang tệp khác?


32

Làm thế nào tôi có thể đạt được

cmd >> file1 2>&1 1>>file2

Đó là, thiết bị xuất chuẩn thiết bị xuất chuẩn nên chuyển hướng đến một tệp (tệp1) và chỉ thiết bị xuất chuẩn (tệp 2) mới nên chuyển hướng sang tệp khác (cả trong chế độ chắp thêm)?

Câu trả lời:


41

Vấn đề là khi bạn chuyển hướng đầu ra của mình, nó không còn khả dụng cho lần chuyển hướng tiếp theo. Bạn có thể chuyển sang teetrong một khung con để giữ đầu ra cho lần chuyển hướng thứ hai:

( cmd | tee -a file2 ) >> file1 2>&1

hoặc nếu bạn muốn xem đầu ra trong terminal:

( cmd | tee -a file2 ) 2>&1 | tee -a file1

Để tránh thêm stderr của lần đầu tiên teevào file1, bạn nên chuyển hướng stderr của lệnh của bạn sang một số mô tả tệp (ví dụ 3), và sau đó thêm điều này vào thiết bị xuất chuẩn:

( 2>&3 cmd | tee -a file2 ) >> file1 3>&1
# or
( 2>&3 cmd | tee -a file2 ) 3>&1 | tee -a file1

(cảm ơn @ fra-san)


16

Với zsh:

cmd >& out+err.log > out.log

Trong chế độ chắp thêm:

cmd >>& out+err.log >> out.log

Trong zshvà được cung cấp mult_iostùy chọn chưa bị vô hiệu hóa, khi một bộ mô tả tệp (ở đây 1) được chuyển hướng nhiều lần để ghi, thì trình bao thực hiện một tích hợp teeđể sao chép đầu ra cho tất cả các mục tiêu.


Tôi không thể tìm ra những gì out+erroutcó nghĩa là ở đây. Tên tập tin? Luồng được chuyển hướng?
gronostaj

@gronostaj Hãy nghĩ rằng lệnh đọccmd >& file1 > file2
Isaac

Giải pháp này sẽ duy trì thứ tự mà đầu ra được tạo ra. Để thực sự lưu trữ thiết bị xuất chuẩn và thiết bị xuất chuẩn (theo thứ tự đó), bạn cần một cách tiếp cận khác.
Isaac

1
@Isaac, thứ tự sẽ không nhất thiết được giữ nguyên vì đầu ra xuất chuẩn sẽ đi qua một đường ống (đến một quá trình chuyển tiếp nó đến từng tệp) trong khi đầu ra stderr sẽ đi trực tiếp vào tệp. Trong mọi trường hợp, có vẻ như OP không yêu cầu đầu ra stderr xuất hiện sau thiết bị xuất chuẩn.
Stéphane Chazelas

3

Bạn có thể: tag stdout (sử dụng sed UNBUFFERED, tức là sed -u ...:), stderr cũng đi đến stdout (không được gắn thẻ, vì nó không đi qua sed gắn thẻ đó), và do đó có thể phân biệt 2 trong logfile kết quả.

Cách sau: chậm (Nó có thể được tối ưu hóa nghiêm túc, bằng cách sử dụng để tạo ra một tập lệnh perl thay vì trong khi ...; làm ...; thực hiện, ví dụ, sẽ sinh ra các chuỗi con & lệnh ở mỗi dòng!), Lạ (có vẻ như tôi cần 2 giai đoạn {} trong một lần đổi tên thiết bị xuất chuẩn và sau đó trong giai đoạn khác thêm "stderr vào" stderr cho nó), v.v. Nhưng đó là: " bằng chứng về khái niệm ", sẽ cố gắng giữ thứ tự đầu ra là hầu hết các thiết bị xuất chuẩn & stderr càng nhiều càng tốt:

#basic principle (some un-necessary "{}" to visually help see the layers):
# { { complex command ;} | sed -e "s/^/TAGstdout/" ;} 2>&1 | read_stdin_and_redispatch

#exemple:
# complex command = a (slowed) ls of several things (some existing, others not)
#  to see if the order of stdout&stderr is kept

#preparation, not needed for the "proof of concept", but needed for our specific exemple setup:
\rm out.file out_AND_err.file unknown unknown2 
touch existing existing2 existing3

#and the (slow, too many execs, etc) "proof of concept":
uniquetag="_stdout_" # change this to something unique, that will NOT appear in all the commands outputs... 
                     # avoid regexp characters ("+" "?" "*" etc) to make it easy to remove with another sed later on.

{
   { for f in existing unknown existing2 unknown2 existing3 ; do ls -l "$f" ; sleep 1; done ;
   } | sed -u -e "s/^/${uniquetag}/" ;
} 2>&1 | while IFS="" read -r line ; do
    case "$line" in
       ${uniquetag}*) printf "%s\n" "$line" | tee -a out_AND_err.file | sed -e "s/^${uniquetag}//" >> out.file ;; 
        *)            printf "%s\n" "$line"       >> out_AND_err.file ;;   
    esac; 
done;

# see the results:
grep "^" out.file out_AND_err.file

Điều này thực sự khó hiểu. (1) Tại sao bạn sử dụng trường hợp sử dụng phức tạp như vậy ( ls unknown) để in một cái gì đó trên stderr? >&2 echo "error"sẽ ổn thôi (2) teecó thể nối thêm nhiều tệp cùng một lúc. (3) Tại sao không chỉ catthay vì grep "^"? (4) tập lệnh của bạn sẽ thất bại khi đầu ra stderr bắt đầu bằng _stdout_. (5) Tại sao?
pLumo

@RoVo: 2 dòng nhận xét đầu tiên hiển thị thuật toán, đơn giản hơn so với bằng chứng về ví dụ khái niệm. 1 ls loop; đuôi thông thường (ví dụ, trên aix.). 3): grep "^" cũng hiển thị cả tên tệp. 4): điều này có thể được thay đổi bởi các biến. 5): ví dụ phức tạp hoạt động trên các ose cũ (ex, aix cũ) nơi tôi đã thử nghiệm nó (không có sẵn perl).
Olivier Dulac

(1) nhiều tiếng vang đến stderr và stout sẽ ổn, nhưng không sao, không quan trọng, sẽ dễ đọc hơn. (3) đồng ý, (4) chắc chắn, nhưng nó sẽ thất bại nếu nó bắt đầu với bất cứ thứ gì mà biến chứa. (5) Tôi thấy.
pLumo

@RoVo Tôi đồng ý với 1 của bạn). cho 4), biến có thể phức tạp đến mức cần thiết để làm cho pb biến mất (ví dụ: uniquetag="banaNa11F453355B28E1158D4E516A2D3EDF96B3450406...)
Olivier Dulac

1
Chắc chắn, nó không có khả năng lắm, nhưng dù sao nó cũng có thể gây ra vấn đề bảo mật sau này. Và sau đó bạn có thể muốn xóa chuỗi đó trước khi in thành tệp ;-)
pLumo

2

Nếu thứ tự đầu ra phải là: stdout thì stderr ; không có giải pháp chỉ với chuyển hướng.
Stderr phải được lưu trữ vào một tập tin tạm thời

cmd 2>>file-err | tee -a file1 >>file2
cat file-err >> file1
rm file-err

Sự miêu tả:

Cách duy nhất để chuyển hướng một đầu ra (một fd như stdout hoặc stderr) sang hai tệp là sao chép nó. Lệnh teenày là công cụ chính xác để tái tạo nội dung mô tả tệp. Vì vậy, một ý tưởng ban đầu để có một đầu ra trên hai tệp sẽ được sử dụng:

... |  tee file1 file2

Điều đó tái tạo stdin của tee cho cả hai tệp (1 & 2) khiến đầu ra của tee vẫn không được sử dụng. Nhưng chúng ta cần phải chắp thêm (sử dụng -a) và chỉ cần một bản sao. Điều này giải quyết cả hai vấn đề:

... | tee -a file1 >>file2

Để cung cấp teevới thiết bị xuất chuẩn (cái cần lặp lại), chúng ta cần tiêu thụ stderr trực tiếp ra khỏi lệnh. Một cách, nếu thứ tự không quan trọng (thứ tự đầu ra sẽ (rất có thể) sẽ được giữ nguyên như được tạo, tùy theo đầu ra nào sẽ được lưu trước). Hoặc:

  1. cmd 2>>file1 | tee -a file2 >>file1
  2. cmd 2>>file1 > >( tee -a file2 >>file1 )
  3. ( cmd | tee -a file2 ) >> file1 2>&1

Tùy chọn 2 chỉ hoạt động trong một số shell. Tùy chọn 3 sử dụng một lớp con bổ sung (chậm hơn) nhưng chỉ sử dụng tên tệp một lần.

Nhưng nếu thiết bị xuất chuẩn phải là đầu tiên (bất kỳ đầu ra thứ tự nào được tạo ra), chúng ta cần lưu trữ stderr để nối nó vào tệp ở cuối (giải pháp đầu tiên được đăng).


1
Hoặc lưu trữ trong bộ nhớ như sponge:(cmd | tee -a out >> out+err) 2>&1 | sponge >> out+err
Stéphane Chazelas

1

Vì lợi ích của sự đa dạng:

Nếu hệ thống của bạn hỗ trợ /dev/stderr, thì

(cmd | tee -a /dev/stderr) 2>> file1 >> file2

sẽ làm việc. Đầu ra tiêu chuẩn của cmd được gửi đến cả thiết bị xuất chuẩn và thiết bị xuất chuẩn của đường ống. Các lỗi tiêu chuẩn của cmdbỏ qua tee và đi ra stderr của đường ống.

Vì thế

  • thiết bị xuất chuẩn của đường ống chỉ là thiết bị xuất chuẩn của cmd
  • stderr của đường ống là stdout và stderr của cmd, xen kẽ.

Đó là một vấn đề đơn giản để gửi các luồng đó đến các tệp chính xác.

Như với hầu hết mọi cách tiếp cận như thế này (bao gồm câu trả lời của Stéphane ), file1có thể khiến các dòng không theo thứ tự.

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.