Ống cho nhiều tập tin trong vỏ


29

Tôi có một ứng dụng sẽ tạo ra một lượng lớn dữ liệu mà tôi không muốn lưu trữ vào đĩa. Ứng dụng chủ yếu xuất dữ liệu mà tôi không muốn sử dụng, nhưng một tập hợp thông tin hữu ích phải được chia thành các tệp riêng biệt. Ví dụ, đưa ra đầu ra sau:

JUNK
JUNK
JUNK
JUNK
A 1
JUNK
B 5
C 1
JUNK

Tôi có thể chạy ứng dụng ba lần như vậy:

./app | grep A > A.out
./app | grep B > B.out
./app | grep C > C.out

Điều này sẽ giúp tôi có được những gì tôi muốn, nhưng nó sẽ mất quá nhiều thời gian. Tôi cũng không muốn chuyển tất cả các kết quả đầu ra vào một tệp và phân tích cú pháp đó.

Có cách nào để kết hợp ba thao tác được hiển thị ở trên theo cách mà tôi chỉ cần chạy ứng dụng một lần mà vẫn nhận được ba tệp đầu ra riêng biệt không?

Câu trả lời:


78

Nếu bạn có tee

./app | tee >(grep A > A.out) >(grep B > B.out) >(grep C > C.out) > /dev/null

(từ đây )

( về thay thế quá trình )


4
Tuyệt vời, điều này cũng có thể được hiển thị dưới dạng:./app | tee >(grep A > A.out) >(grep B > B.out) | grep C > C.out
evilsoup

7
Câu trả lời này hiện là câu trả lời chính xác duy nhất, với tiêu đề ban đầu là "đường ống cho nhiều quy trình".
acelent

3
+1. Đây là câu trả lời thường được áp dụng nhất, vì nó không phụ thuộc vào thực tế là lệnh lọc cụ thể grep.
ruakh

1
Tôi đồng ý rằng đây là câu trả lời tốt nhất cho câu hỏi được đặt ra và nên được đánh dấu như vậy. Song song là một giải pháp khác (như đã đăng) nhưng đã thực hiện một số so sánh đúng thời gian, ví dụ trên có hiệu quả hơn. Thay vào đó, nếu op liên quan đến các hoạt động chuyên sâu cpu cao như nén nhiều tệp hoặc chuyển đổi nhiều mp3 thì không có nghi ngờ gì, giải pháp song song sẽ chứng minh được hiệu quả hơn.
AsymLabs

32

Bạn có thể dùng awk

./app | awk '/A/{ print > "A.out"}; /B/{ print > "B.out"}; /C/{ print > "C.out"}'

6
Tiêu đề của câu hỏi là dẫn đến nhiều quy trình , câu trả lời này là về "đường ống" (gửi theo regex) đến nhiều tệp . Vì câu trả lời này đã được chấp nhận, nên thay đổi tiêu đề của câu hỏi.
acelent

@PauloMadeira Bạn nói đúng. Bạn nghĩ gì sẽ là một tiêu đề tốt hơn?
sj755

Tôi đã đề xuất một chỉnh sửa rất nhỏ "Chuyển sang nhiều tệp trong trình bao", đó là bản sửa đổi đang chờ xử lý, hãy kiểm tra nó. Tôi đã mong đợi để loại bỏ bình luận nếu nó được chấp nhận.
acelent

@PauloMadeira - Tôi đã thay đổi tiêu đề. Không thấy chỉnh sửa của bạn, nhưng bạn đã đúng, việc sử dụng các quy trình trong tiêu đề là không chính xác nếu đây là câu trả lời được chấp nhận.
slm

17

Bạn cũng có thể sử dụng các khả năng khớp mẫu của shell :

./app | while read line; do 
     [[ "$line" =~ A ]] && echo $line >> A.out; 
     [[ "$line" =~ B ]] && echo $line >> B.out; 
     [[ "$line" =~ C ]] && echo $line >> C.out; 
 done

Hoặc thậm chí:

./app | while read line; do for foo in A B C; do 
     [[ "$line" =~ "$foo" ]] && echo $line >> "$foo".out; 
  done; done

Một cách an toàn hơn có thể xử lý dấu gạch chéo ngược và dòng bắt đầu bằng -:

./app | while IFS= read -r line; do for foo in A B C; do 
     [[ "$line" =~ "$foo" ]] && printf -- "$line\n" >> "$foo".out; 
  done; done

Như @StephaneChazelas chỉ ra trong các bình luận, điều này không hiệu quả lắm. Giải pháp tốt nhất có lẽ là @ AurélienOoms ' .


Giả sử đầu vào không chứa dấu gạch chéo ngược hoặc khoảng trắng hoặc ký tự đại diện hoặc dòng bắt đầu -n, -e... Nó cũng sẽ không hiệu quả khủng khiếp vì nó có nghĩa là một số cuộc gọi hệ thống trên mỗi dòng (một read(2)ký tự, tệp đang mở, viết đóng cho mỗi dòng ...). Nói chung, sử dụng while readcác vòng lặp để xử lý văn bản trong shell là thực tế xấu.
Stéphane Chazelas

@StephaneChazelas Tôi đã chỉnh sửa câu trả lời của mình. Nó sẽ hoạt động với dấu gạch chéo ngược và -nvv. Theo như tôi có thể nói cả hai phiên bản đều hoạt động tốt với các khoảng trống, tôi có sai không?
terdon

Không, đối số đầu tiên printflà định dạng. Không có lý do để để lại cho bạn các biến không được trích dẫn trong đó.
Stéphane Chazelas

Điều này cũng sẽ phá vỡ trong bash (và các shell khác sử dụng cstrings theo cách tương tự) nếu có null trong đầu vào.
Chris Xuống

9

Nếu bạn có nhiều lõi và bạn muốn các quá trình song song, bạn có thể làm:

parallel -j 3 -- './app | grep A > A.out' './app | grep B > B.out' './app | grep C > C.out'

Điều này sẽ sinh ra ba quá trình trong lõi song song. Nếu bạn muốn có một số đầu ra cho bàn điều khiển hoặc một tệp chủ, nó có lợi thế là giữ đầu ra theo một thứ tự nào đó, thay vì trộn nó.

Tiện ích gnu song song từ Ole Tange có thể được lấy từ hầu hết các repos dưới tên song song hoặc moreutils . Nguồn có thể được lấy từ Savannah.gnu.org . Ngoài ra một video hướng dẫn giới thiệu ở đây .

Phụ lục

Sử dụng phiên bản song song gần đây hơn (không nhất thiết là phiên bản trong repo phân phối của bạn), bạn có thể sử dụng cấu trúc thanh lịch hơn:

./app | parallel -j3 -k --pipe 'grep {1} >> {1}.log' ::: 'A' 'B' 'C'

Việc đạt được kết quả của việc chạy một ./app và 3 quy trình grep song song trong các lõi hoặc luồng riêng biệt (như được xác định bởi chính nó, cũng coi -j3 là tùy chọn, nhưng nó được cung cấp trong ví dụ này cho mục đích hướng dẫn).

Phiên bản mới hơn của song song có thể thu được bằng cách thực hiện:

wget http://ftpmirror.gnu.org/parallel/parallel-20131022.tar.bz2

Sau đó, giải nén thông thường, cd thành song song- {ngày}, ./có hình && make, sudo thực hiện cài đặt. Điều này sẽ cài đặt song song, trang man song song và trang manallel_tutorial.


7

Đây là một trong Perl:

./app | perl -ne 'BEGIN {open(FDA, ">A.out") and 
                         open(FDB, ">B.out") and 
                         open(FDC, ">C.out") or die("Cannot open files: $!\n")} 
                  print FDA $_ if /A/; print FDB $_ if /B/; print FDC $_ if /C/'

1
sed -ne/A/w\ A.out -e/B/w\ B.out -e/C/p <in >C.out

... Nếu <incó thể đọc được, cả ba tệp dữ liệu sẽ bị cắt trước khi mọi thứ được viết cho chúng.

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.