it-swarm-vi.com

Quá trình thay thế và đường ống

Tôi đã tự hỏi làm thế nào để hiểu những điều sau đây:

Đặt ống dẫn của lệnh vào stdin của người khác là một kỹ thuật mạnh mẽ. Nhưng, điều gì sẽ xảy ra nếu bạn cần bỏ qua các thiết bị xuất chuẩn của nhiều lệnh? Đây là nơi thay thế quá trình đến.

Nói cách khác, quá trình thay thế có thể làm bất cứ điều gì ống có thể làm?

Những gì có thể xử lý thay thế làm, nhưng ống không thể?

89
Tim

Một cách hay để tìm ra sự khác biệt giữa chúng là thử nghiệm một chút trên dòng lệnh. Mặc dù có sự tương đồng về hình ảnh khi sử dụng < nhân vật, nó làm một cái gì đó rất khác với một chuyển hướng hoặc đường ống.

Hãy sử dụng lệnh date để kiểm tra.

$ date | cat
Thu Jul 21 12:39:18 EEST 2011

Đây là một ví dụ vô nghĩa nhưng nó cho thấy cat đã chấp nhận đầu ra của date trên STDIN và nhổ nó ra. Các kết quả tương tự có thể đạt được bằng cách thay thế quá trình:

$ cat <(date)
Thu Jul 21 12:40:53 EEST 2011

Tuy nhiên những gì chỉ xảy ra đằng sau hậu trường là khác nhau. Thay vì được cung cấp một luồng STDIN, cat thực sự đã được thông qua tên của một tệp mà nó cần để mở và đọc. Bạn có thể thấy bước này bằng cách sử dụng echo thay vì cat.

$ echo <(date)
/proc/self/fd/11

Khi mèo nhận được tên tệp, nó đọc nội dung của tệp cho chúng tôi. Mặt khác, echo chỉ cho chúng ta thấy tên tệp mà nó đã được thông qua. Sự khác biệt này trở nên rõ ràng hơn nếu bạn thêm nhiều sự thay thế:

$ cat <(date) <(date) <(date)
Thu Jul 21 12:44:45 EEST 2011
Thu Jul 21 12:44:45 EEST 2011
Thu Jul 21 12:44:45 EEST 2011

$ echo <(date) <(date) <(date)
/proc/self/fd/11 /proc/self/fd/12 /proc/self/fd/13

Có thể kết hợp thay thế quá trình (tạo tệp) và chuyển hướng đầu vào (kết nối tệp với STDIN):

$ cat < <(date)
Thu Jul 21 12:46:22 EEST 2011

Nó trông khá giống nhau nhưng lần này con mèo đã được truyền luồng STDIN thay vì tên tệp. Bạn có thể thấy điều này bằng cách thử nó với echo:

$ echo < <(date)
<blank>

Vì echo không đọc STDIN và không có đối số nào được thông qua, chúng tôi không nhận được gì.

Các đường ống và đầu vào chuyển hướng nội dung xô vào luồng STDIN. Quá trình thay thế chạy các lệnh, lưu đầu ra của chúng vào một tệp tạm thời đặc biệt và sau đó chuyển tên tệp đó thay cho lệnh. Bất cứ lệnh nào bạn đang sử dụng đều coi nó như một tên tệp. Lưu ý rằng tệp được tạo không phải là tệp thông thường mà là đường ống có tên sẽ tự động bị xóa sau khi không còn cần thiết nữa.

140
Caleb

Dưới đây là ba điều bạn có thể làm với quá trình thay thế là không thể.

Nhiều quá trình đầu vào

diff <(cd /foo/bar/; ls) <(cd /foo/baz; ls)

Đơn giản là không có cách nào để làm điều này với đường ống.

Bảo quản STDIN

Nói rằng bạn có những điều sau đây:

curl -o - http://example.com/script.sh
   #/bin/bash
   read LINE
   echo "You said ${LINE}!"

Và bạn muốn chạy nó trực tiếp. Sau đây thất bại thảm hại. Bash đã sử dụng STDIN để đọc tập lệnh, vì vậy không thể nhập dữ liệu khác.

curl -o - http://example.com/script.sh | bash 

Nhưng cách này hoạt động hoàn hảo.

bash <(curl -o - http://example.com/script.sh)

Thay thế quá trình ra nước ngoài

Cũng lưu ý rằng quá trình thay thế hoạt động theo cách khác quá. Vì vậy, bạn có thể làm một cái gì đó như thế này:

(ls /proc/*/exe >/dev/null) 2> >(sed -n \
  '/Permission denied/ s/.*\(\/proc.*\):.*/\1/p' > denied.txt )

Đó là một ví dụ phức tạp, nhưng nó sẽ gửi stdout đến /dev/null, trong khi đường ống stderr vào tập lệnh sed để trích xuất tên của các tệp mà lỗi "Quyền bị từ chối" được hiển thị, sau đó gửi kết quả THOSE đến một tệp.

Lưu ý rằng lệnh đầu tiên và stdout chuyển hướng nằm trong ngoặc đơn (subshell) để chỉ kết quả của lệnh THAT được gửi đến /dev/null và nó không gây rối với phần còn lại của dòng.

26
tylerl

Tôi nên giả sử bạn đang nói về bash hoặc một số Shell nâng cao khác, bởi vì Shell posix không có thay thế quá trình.

bash báo cáo trang thủ công:

Quy trình thay thế
[.___.] Quá trình thay thế được hỗ trợ trên các hệ thống hỗ trợ các đường ống có tên (FIFO) hoặc phương thức/dev/fd để đặt tên các tệp đang mở. Nó có dạng <(danh sách) hoặc> (danh sách). Danh sách quy trình được chạy với đầu vào hoặc đầu ra được kết nối với a FIFO hoặc một số tệp trong/dev/fd. Tên của tệp này được truyền dưới dạng đối số cho lệnh hiện tại là kết quả của Nếu sử dụng biểu mẫu <(danh sách), ghi vào tệp sẽ cung cấp đầu vào cho danh sách. Nếu biểu mẫu <(danh sách) được sử dụng, nên đọc tệp dưới dạng đối số để lấy đầu ra của danh sách.

[.___.] Khi có sẵn, quá trình thay thế được thực hiện đồng thời với mở rộng tham số và biến, thay thế lệnh và mở rộng số học.

Nói cách khác, và từ quan điểm thực tế, bạn có thể sử dụng một biểu thức như sau

<(commands)

làm tên tệp cho các lệnh khác yêu cầu tệp làm tham số. Hoặc bạn có thể sử dụng chuyển hướng cho một tệp như vậy:

while read line; do something; done < <(commands)

Quay trở lại câu hỏi của bạn, dường như với tôi rằng quá trình thay thế và đường ống không có nhiều điểm chung.

Nếu bạn muốn sắp xếp theo thứ tự đầu ra của nhiều lệnh, bạn có thể sử dụng một trong các hình thức sau:

(command1; command2) | command3
{ command1; command2; } | command3

nhưng bạn cũng có thể sử dụng chuyển hướng để thay thế quá trình

command3 < <(command1; command2)

cuối cùng, nếu command3 chấp nhận tham số tệp (thay thế stdin)

command3 <(command1; command2)
26
enzotib

Nếu một lệnh lấy danh sách các tệp làm đối số và xử lý các tệp đó làm đầu vào (hoặc đầu ra, nhưng không phổ biến), thì mỗi tệp đó có thể là một ống có tên hoặc/dev/fd tệp giả được cung cấp trong suốt theo quy trình:

$ sort -m <(command1) <(command2) <(command3)

Điều này sẽ "dẫn" đầu ra của ba lệnh để sắp xếp, vì sắp xếp có thể lấy danh sách các tệp đầu vào trên dòng lệnh.

10
camh

Cần lưu ý rằng quá trình thay thế không bị giới hạn ở dạng <(command), sử dụng đầu ra của command dưới dạng tệp. Nó có thể ở dạng >(command) cung cấp một tệp làm đầu vào cho command. Điều này cũng được đề cập trong trích dẫn của hướng dẫn bash trong câu trả lời của @ enzotib.

Đối với ví dụ date | cat Ở trên, một lệnh sử dụng thay thế quy trình của biểu mẫu >(command) để đạt được hiệu ứng tương tự sẽ là,

date > >(cat)

Lưu ý rằng > Trước >(cat) là cần thiết. Điều này một lần nữa có thể được minh họa rõ ràng bằng echo như trong câu trả lời của @ Caleb.

$ echo >(cat)
/dev/fd/63

Vì vậy, nếu không có thêm >, date >(cat) sẽ giống như date /dev/fd/63 Sẽ in một thông báo tới stderr.

Giả sử bạn có một chương trình chỉ lấy tên tệp làm tham số và không xử lý stdin hoặc stdout. Tôi sẽ sử dụng tập lệnh đơn giản hóa psub.sh Để minh họa điều này. Nội dung của psub.sh

#!/bin/bash
[ -e "$1" -a -e "$2" ] && awk '{print $1}' "$1" > "$2"

Về cơ bản, nó kiểm tra cả hai đối số của nó là các tệp (không nhất thiết là các tệp thông thường) và nếu đây là trường hợp, hãy viết trường đầu tiên của mỗi dòng "$1" Thành "$2" Bằng cách sử dụng awk. Sau đó, một lệnh kết hợp tất cả những gì được đề cập cho đến nay là,

./psub.sh <(printf "a a\nc c\nb b") >(sort)

Cái này sẽ in

a
b
c

và tương đương với

printf "a a\nc c\nb b" | awk '{print $1}' | sort

nhưng những điều sau đây sẽ không hoạt động và chúng ta phải sử dụng thay thế quá trình ở đây,

printf "a a\nc c\nb b" | ./psub.sh | sort

hoặc hình thức tương đương của nó

printf "a a\nc c\nb b" | ./psub.sh /dev/stdin /dev/stdout | sort

Nếu ./psub.sh Cũng đọc stdin bên cạnh những gì được đề cập ở trên, thì một dạng tương đương như vậy không tồn tại và trong trường hợp đó, chúng tôi không thể sử dụng gì thay thế cho quá trình thay thế (tất nhiên bạn có thể cũng sử dụng một tập tin ống hoặc temp có tên, nhưng đó là một câu chuyện khác).

3
Weijun Zhou