Làm cách nào để chụp STDOUT / STDERR theo thứ tự và thêm dấu thời gian / tiền tố?


25

Tôi đã khám phá hầu hết tất cả các câu hỏi tương tự có sẵn , nhưng không có kết quả.

Hãy để tôi mô tả chi tiết vấn đề:

Tôi chạy một số tập lệnh không giám sát và chúng có thể tạo ra các dòng lỗi tiêu chuẩn và đầu ra tiêu chuẩn, tôi muốn nắm bắt chúng theo thứ tự chính xác như được hiển thị bởi trình giả lập thiết bị đầu cuối và sau đó thêm tiền tố như "STDERR:" và "STDOUT:" cho chúng.

Tôi đã thử sử dụng các đường ống và thậm chí là cách tiếp cận dựa trên epoll trên chúng, nhưng không có kết quả. Tôi nghĩ rằng giải pháp là trong việc sử dụng pty, mặc dù tôi không phải là bậc thầy về điều đó. Tôi cũng đã xem qua mã nguồn của Vome của Gnome , nhưng điều đó không mang lại nhiều hiệu quả.

Lý tưởng nhất là tôi sẽ sử dụng Go thay vì Bash để thực hiện điều này, nhưng tôi không thể làm được. Có vẻ như các đường ống tự động cấm giữ một trật tự dòng chính xác vì bộ đệm.

Có ai đó đã có thể làm một cái gì đó tương tự? Hay nó chỉ là không thể? Tôi nghĩ rằng nếu một trình giả lập thiết bị đầu cuối có thể làm điều đó, thì nó không - có thể bằng cách tạo một chương trình C nhỏ xử lý PTY (s) khác nhau?

Lý tưởng nhất là tôi sẽ sử dụng đầu vào không đồng bộ để đọc 2 luồng này (STDOUT và STDERR) và sau đó in lại chúng theo nhu cầu của tôi, nhưng thứ tự đầu vào là rất quan trọng!

LƯU Ý: Tôi biết về stderred nhưng nó không hoạt động với tôi với các tập lệnh Bash và không thể dễ dàng chỉnh sửa để thêm tiền tố (vì về cơ bản nó bao bọc rất nhiều tòa nhà).

Cập nhật: thêm vào dưới hai ý chính

(độ trễ ngẫu nhiên phụ thứ hai có thể được thêm vào trong tập lệnh mẫu tôi đã cung cấp để chứng minh một kết quả nhất quán)

Cập nhật: giải pháp cho câu hỏi này cũng sẽ giải quyết câu hỏi khác này , như @Gilles đã chỉ ra. Tuy nhiên tôi đã đi đến kết luận rằng không thể làm những gì được hỏi ở đây và đó. Khi sử dụng 2>&1cả hai luồng được hợp nhất chính xác ở cấp độ pty / ống, nhưng để sử dụng các luồng riêng biệt và theo đúng thứ tự, người ta thực sự nên sử dụng cách tiếp cận của stderred mà involes s hookall hooking và có thể bị coi là bẩn theo nhiều cách.

Tôi sẽ háo hức cập nhật câu hỏi này nếu ai đó có thể không bảo vệ những điều trên.


1
Đây không phải là những gì bạn muốn? stackoverflow.com/questions/21564/ cường
slm

@slm có lẽ là không, vì OP cần phải thêm các chuỗi khác nhau vào các luồng khác nhau.
peterph

Bạn có thể chia sẻ lý do tại sao thứ tự rất quan trọng? Có lẽ có thể có một số cách khác xung quanh vấn đề của bạn ...
peterph

@peterph đó là một điều kiện tiên quyết, nếu tôi không thể có đầu ra nhất quán, tôi muốn gửi nó tới / dev / null hơn là đọc nó và bị nhầm lẫn bởi nó :) 2> & 1 giữ nguyên thứ tự, nhưng không cho phép loại tùy chỉnh mà tôi hỏi trong câu hỏi này
Deim0s

Câu trả lời:


12

Bạn có thể sử dụng coprocesses. Trình bao bọc đơn giản cung cấp cả hai kết quả đầu ra của một lệnh đã cho cho hai sedtrường hợp (một cho stderrtrường hợp khác stdout), thực hiện gắn thẻ.

#!/bin/bash
exec 3>&1
coproc SEDo ( sed "s/^/STDOUT: /" >&3 )
exec 4>&2-
coproc SEDe ( sed "s/^/STDERR: /" >&4 )
eval $@ 2>&${SEDe[1]} 1>&${SEDo[1]}
eval exec "${SEDo[1]}>&-"
eval exec "${SEDe[1]}>&-"

Lưu ý một số điều:

  1. Đó là một câu thần chú cho nhiều người (bao gồm cả tôi) - vì một lý do (xem câu trả lời được liên kết dưới đây).

  2. Không có gì đảm bảo đôi khi nó sẽ hoán đổi vài dòng - tất cả phụ thuộc vào lịch trình của các bộ đồng xử lý. Trên thực tế, nó gần như được đảm bảo rằng tại một số thời điểm nó sẽ. Điều đó nói rằng, nếu giữ trật tự hoàn toàn giống nhau, bạn phải xử lý dữ liệu từ cả hai stderrstdintrong cùng một quy trình, nếu không, bộ lập lịch kernel có thể (và sẽ) tạo ra một mớ hỗn độn.

    Nếu tôi hiểu chính xác vấn đề, điều đó có nghĩa là bạn sẽ cần phải hướng dẫn trình vỏ chuyển hướng cả hai luồng sang một quy trình (có thể được thực hiện AFAIK). Rắc rối bắt đầu khi quá trình đó bắt đầu quyết định hành động nào trước - nó sẽ phải thăm dò cả hai nguồn dữ liệu và đến một lúc nào đó sẽ chuyển sang trạng thái xử lý một luồng và dữ liệu đến cả hai luồng trước khi kết thúc. Và đó chính xác là nơi nó bị phá vỡ. Điều đó cũng có nghĩa là, việc bao bọc các tòa nhà đầu ra như thế stderredcó lẽ là cách duy nhất để đạt được kết quả mong muốn của bạn (và thậm chí sau đó bạn có thể gặp sự cố khi một cái gì đó trở nên đa luồng trên hệ thống đa bộ xử lý).

Theo như coprocesses, hãy chắc chắn đọc câu trả lời xuất sắc của Stéphane trong Làm thế nào để bạn sử dụng lệnh coproc trong Bash? cho cái nhìn sâu sắc.


Cảm ơn @peterph cho câu trả lời của bạn, tuy nhiên tôi đang tìm cách đặc biệt để giữ gìn trật tự. Lưu ý: Tôi nghĩ rằng trình thông dịch của bạn nên được bash vì thay thế quy trình bạn sử dụng (Tôi nhận được ./test1.sh: 3: ./test1.sh: Syntax error: "(" unexpectedbằng cách sao chép / dán tập lệnh của bạn)
Deim0s

Rất có thể là như vậy, tôi đã chạy nó bashvới /bin/sh(không biết tại sao tôi lại có nó ở đó).
peterph

Tôi đã cập nhật câu hỏi một chút, về nơi có thể xảy ra sự trộn lẫn luồng.
peterph

1
eval $@là khá lỗi. Sử dụng "$@"nếu bạn muốn chạy các đối số của mình dưới dạng một dòng lệnh chính xác - thêm một lớp evaldiễn giải sẽ ném vào một loạt các điều khó dự đoán (và có khả năng gây hại, nếu bạn chuyển tên tệp hoặc nội dung khác mà bạn không kiểm soát như đối số) hành vi và không trích dẫn ngay cả moreso (chia tên với khoảng trắng thành nhiều từ, mở rộng sự ảm đạm ngay cả khi trước đây chúng được trích dẫn là bằng chữ, v.v.).
Charles Duffy

1
Ngoài ra, trong bash hiện đại đủ để có đồng xử lý, bạn không cần eval phải đóng các mô tả tệp có tên trong một biến. exec {SEDo[1]}>&-sẽ hoạt động như bình thường (vâng, việc thiếu một $trước khi {cố tình).
Charles Duffy

5

Phương pháp # 1. Sử dụng mô tả tập tin và awk

Điều gì về một cái gì đó như thế này bằng cách sử dụng các giải pháp từ Hỏi & Đáp SO này có tiêu đề: Có tiện ích Unix để thêm dấu thời gian cho dòng văn bản không? và câu hỏi SO này có tiêu đề: ống STDOUT và STDERR cho hai quy trình khác nhau trong tập lệnh shell? .

Cách tiếp cận

Bước 1, chúng ta tạo 2 hàm trong Bash sẽ thực hiện thông báo dấu thời gian khi được gọi:

$ msgOut () {  awk '{ print strftime("STDOUT: %Y-%m-%d %H:%M:%S"), $0; fflush(); }'; }
$ msgErr () {  awk '{ print strftime("STDERR: %Y-%m-%d %H:%M:%S"), $0; fflush(); }'; }

Bước 2 bạn sẽ sử dụng các chức năng trên như vậy để nhận được tin nhắn mong muốn:

$ { { { ...command/script... } 2>&3; } 2>&3 | msgErr; } 3>&1 1>&2 | msgOut

Thí dụ

Ở đây tôi đã tạo ra một ví dụ sẽ ghi avào STDOUT, ngủ trong 10 giây và sau đó ghi đầu ra vào STDERR. Khi chúng tôi đặt chuỗi lệnh này vào cấu trúc của chúng tôi ở trên, chúng tôi nhận được tin nhắn như bạn đã chỉ định.

$ { { echo a; sleep 10; echo >&2 b; } 2>&3 | \
    msgErr; } 3>&1 1>&2 | msgOut
STDERR: 2014-09-26 09:22:12 a
STDOUT: 2014-09-26 09:22:22 b

Phương pháp # 2. Sử dụng chú thích đầu ra

Có một công cụ được gọi là annotate-outputmột phần của devscriptsgói sẽ làm những gì bạn muốn. Hạn chế duy nhất là nó phải chạy các kịch bản cho bạn.

Thí dụ

Nếu chúng ta đặt chuỗi lệnh ví dụ trên của chúng ta vào một tập lệnh được gọi mycmds.bashnhư vậy:

$ cat mycmds.bash 
#!/bin/bash

echo a
sleep 10
echo >&2 b

Sau đó chúng ta có thể chạy nó như thế này:

$ annotate-output ./mycmds.bash 
09:48:00 I: Started ./mycmds.bash
09:48:00 O: a
09:48:10 E: b
09:48:10 I: Finished with exitcode 0

Định dạng của đầu ra có thể được kiểm soát cho phần dấu thời gian nhưng không vượt quá điều đó. Nhưng nó tương tự như sản phẩm bạn đang tìm kiếm, vì vậy nó có thể phù hợp với hóa đơn.


1
Thật không may, điều này cũng không giải quyết được vấn đề có thể hoán đổi một số dòng.
peterph

chính xác. Tôi nghĩ rằng câu trả lời cho câu hỏi này của tôi là "không thể". Sự kiện với stderredbạn không thể dễ dàng xác định ranh giới của các dòng (cố gắng như vậy sẽ bị hack). Tôi muốn xem liệu ai đó có thể giúp tôi với vấn đề này không nhưng dường như mọi người đều muốn từ bỏ một ràng buộc ( thứ tự ) duy nhất làm cơ sở cho câu hỏi
Deim0s

Bước 2 của Phương pháp 1 yêu cầu một {ở phía trước để hoạt động đúng.
Austin Hanson
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.