Câu hỏi Sự khác biệt giữa "Chuyển hướng" và "Ống" là gì?


Câu hỏi này có vẻ hơi ngu ngốc, nhưng tôi không thể thấy sự khác biệt giữa chuyển hướng và đường ống.

Chuyển hướng được sử dụng để chuyển hướng stdout / stdin / stderr, ví dụ: ls > log.txt.

Các đường ống được sử dụng để cung cấp đầu ra của một lệnh làm đầu vào cho một lệnh khác, ví dụ: ls | grep file.txt.

Nhưng tại sao có hai nhà khai thác cho cùng một điều?

Tại sao không chỉ viết ls > grep để vượt qua đầu ra thông qua, không phải đây chỉ là một loại chuyển hướng? Những gì tôi đang mất tích?


174
2017-08-07 13:22


gốc




Các câu trả lời:


Ống được sử dụng để vượt qua đầu ra khác chương trình hoặc tiện ích.

Chuyển hướng được sử dụng để truyền đầu ra cho một tệp hoặc luồng.

Thí dụ: thing1 > thing2 so với thing1 | thing2

thing1 > thing2

  1. Shell của bạn sẽ chạy chương trình có tên thing1
  2. Mọi thứ thing1 đầu ra sẽ được đặt trong một tệp gọi là thing2. (Lưu ý - nếu thing2 tồn tại, nó sẽ bị ghi đè)

Nếu bạn muốn vượt qua đầu ra từ chương trình thing1 cho một chương trình được gọi là thing2, bạn có thể làm như sau:

thing1 > temp_file && thing2 < temp_file

mà sẽ

  1. chạy chương trình có tên thing1
  2. lưu đầu ra vào một tệp có tên temp_file
  3. chạy chương trình có tên thing2, giả vờ rằng người ở bàn phím đã nhập nội dung của temp_file làm đầu vào.

Tuy nhiên, đó là điều khó khăn, vì vậy họ đã làm cho đường ống như một cách đơn giản hơn để làm điều đó. thing1 | thing2 làm điều tương tự như thing1 > temp_file && thing2 < temp_file

CHỈNH SỬA để cung cấp thêm chi tiết cho câu hỏi trong nhận xét:

Nếu > cố gắng để được cả hai "vượt qua chương trình" và "ghi vào tập tin", nó có thể gây ra vấn đề trong cả hai hướng.

Ví dụ đầu tiên: Bạn đang cố ghi vào một tập tin. Đã tồn tại một tệp có tên mà bạn muốn ghi đè lên. Tuy nhiên, tệp có thể thực thi được. Có lẽ, nó sẽ cố gắng thực hiện tập tin này, đi qua đầu vào. Bạn sẽ phải làm một cái gì đó như viết đầu ra cho một tên tập tin mới, sau đó đổi tên tập tin.

Ví dụ thứ hai: Như Florian Diesch đã chỉ ra, nếu có một lệnh khác ở đâu đó trong hệ thống có cùng tên (có nghĩa là trong đường dẫn thực hiện). Nếu bạn dự định tạo một tệp có tên đó trong thư mục hiện tại của mình, bạn sẽ bị kẹt.

Thứ ba: nếu bạn gõ sai lệnh, nó sẽ không cảnh báo bạn rằng lệnh đó không tồn tại. Ngay bây giờ, nếu bạn gõ ls | gerp log.txt nó sẽ cho bạn biết bash: gerp: command not found. Nếu > có nghĩa là cả hai, nó sẽ chỉ đơn giản là tạo ra một tập tin mới cho bạn (sau đó cảnh báo nó không biết phải làm gì với log.txt).


195
2017-08-07 13:30



Cảm ơn bạn. Bạn đã đề cập thing1 > temp_file && thing2 < temp_fileđể làm dễ dàng hơn với đường ống. Nhưng tại sao không sử dụng lại > nhà điều hành để thực hiện điều này, ví dụ: thing1 > thing2 cho lệnh thing1 và thing2 ? Tại sao một nhà điều hành bổ sung | ? - John Threepwood
"Lấy đầu ra và ghi nó vào một tập tin" là một hành động khác với "Lấy đầu ra và chuyển nó đến một chương trình khác". Tôi sẽ chỉnh sửa thêm suy nghĩ vào câu trả lời của tôi ... - David Oneill
@ JohnHreepwood Họ có ý nghĩa khác nhau. Điều gì sẽ xảy ra nếu tôi muốn chuyển hướng nội dung nào đó đến tệp có tên less, ví dụ? thing | less và thing > less hoàn toàn khác nhau, vì chúng làm những việc khác nhau. Những gì bạn đề xuất sẽ tạo ra một sự mơ hồ. - Darkhogg
Có chính xác khi nói rằng "thing1> temp_file" chỉ đơn thuần là cú pháp đường cho "thing1 | tee temp_file"? Kể từ khi tìm hiểu về tee tôi hầu như không bao giờ sử dụng chuyển hướng. - Sridhar-Sarnobat
@ Sridhar-Sarnobat no, tee lệnh nào khác. tee viết đầu ra cho cả màn hình (stdout) và tập tin. Chuyển hướng nào chỉ có tập tin. - David Oneill


Nếu ý nghĩa của foo > bar sẽ phụ thuộc vào việc có một lệnh có tên bar điều đó sẽ khiến việc sử dụng chuyển hướng trở nên khó khăn hơn và dễ bị lỗi hơn: Mỗi khi tôi muốn chuyển hướng đến một tệp, trước tiên tôi phải kiểm tra xem có lệnh có tên là tệp đích của tôi không.


19
2017-08-07 13:40



Đây chỉ là vấn đề nếu bạn đang viết thư cho bar trong một thư mục thuộc về $PATH biến env. Nếu bạn đang ở trong một cái gì đó giống như / bin, sau đó ot có thể là một vấn đề. Nhưng ngay cả sau đó, bar sẽ phải có quyền thực thi được thiết lập để vỏ kiểm tra không chỉ để tìm kiếm tệp thực thi bar nhưng thực sự có thể thực thi nó. Và nếu mối quan tâm là ghi đè tệp hiện có, noclober tùy chọn vỏ nên ngăn ghi đè tệp hiện có trong chuyển hướng. - Sergiy Kolodyazhnyy


Có một sự khác biệt quan trọng giữa hai nhà khai thác:

  1. ls > log.txt -> Lệnh này sẽ gửi đầu ra tới tệp log.txt.

  2. ls | grep file.txt -> Lệnh này gửi đầu ra của lệnh ls tới grep thông qua việc sử dụng đường ống (|), và lệnh grep tìm kiếm tệp.txt trong phần nhập được cung cấp bởi lệnh trước đó.

Nếu bạn phải thực hiện nhiệm vụ tương tự bằng kịch bản đầu tiên, thì nó sẽ là:

ls > log.txt; grep 'file.txt' log.txt

Vì vậy, một đường ống (với |) được sử dụng để gửi đầu ra đến lệnh khác, trong khi chuyển hướng (với >) được sử dụng để chuyển hướng đầu ra tới một số tệp.


11
2017-08-07 13:32





Từ Sổ tay quản trị hệ thống UNIX và Linux:

Chuyển hướng

Trình bao diễn giải các ký hiệu <,> và >> làm hướng dẫn định tuyến lại lệnh của đầu vào hoặc đầu ra đến hoặc từ tập tin.

Ống

Để kết nối STDOUT của một chỉ huy tới STDIN của khác sử dụng | biểu tượng, thường được gọi là đường ống.

Vì vậy, giải thích của tôi là: Nếu đó là lệnh để chỉ huy, sử dụng một đường ống. Nếu bạn xuất ra hoặc từ một tập tin sử dụng chuyển hướng.


9
2018-02-16 00:40





Có một sự khác biệt về cú pháp lớn giữa hai:

  1. Chuyển hướng là một đối số cho một chương trình
  2. Một đường ống tách hai lệnh

Bạn có thể nghĩ về các chuyển hướng như thế này: cat [<infile] [>outfile]. Điều này ngụ ý trật tự không quan trọng: cat <infile >outfile giống như cat >outfile <infile. Bạn thậm chí có thể trộn chuyển hướng lên với các đối số khác: cat >outfile <infile -b và cat <infile -b >outfile cả hai đều hoàn toàn ổn. Ngoài ra bạn có thể chuỗi với nhau nhiều hơn một đầu vào hoặc đầu ra (đầu vào sẽ được đọc tuần tự và tất cả các đầu ra sẽ được ghi vào mỗi tập tin đầu ra): cat >outfile1 >outfile2 <infile1 <infile2. Mục tiêu hoặc nguồn của chuyển hướng có thể là tên tệp hoặc tên của luồng (như & 1, ít nhất là trong bash).

Nhưng các đường ống hoàn toàn tách biệt một lệnh khỏi một lệnh khác, bạn không thể kết hợp chúng với các đối số:

[command1] | [command2]

Đường ống lấy mọi thứ được ghi vào đầu ra tiêu chuẩn từ command1 và gửi nó tới đầu vào chuẩn của lệnh2.

Bạn cũng có thể kết hợp đường ống và chuyển hướng. Ví dụ:

cat <infile >outfile | cat <infile2 >outfile2

Đầu tiên cat sẽ đọc dòng từ infile, sau đó đồng thời viết mỗi dòng để outfile và gửi nó đến lần thứ hai cat.

Trong lần thứ hai cat, đầu vào tiêu chuẩn đầu tiên đọc từ các đường ống (nội dung của infile), sau đó đọc từ infile2, viết mỗi dòng để outfile2. Sau khi chạy này, outfile sẽ là một bản sao của infile, và outfile2 sẽ chứa infile theo sau bởi infile2.

Cuối cùng, bạn thực sự làm một cái gì đó thực sự tương tự như ví dụ của bạn bằng cách sử dụng chuyển hướng "ở đây chuỗi" (chỉ dành cho gia đình bash) và backticks:

grep blah <<<`ls`

sẽ cho kết quả tương tự như

ls | grep blah

Nhưng tôi nghĩ phiên bản chuyển hướng đầu tiên sẽ đọc tất cả đầu ra của ls vào bộ đệm (trong bộ nhớ), và sau đó nạp bộ đệm đó vào grep một dòng, trong khi phiên bản đường ống sẽ lấy mỗi dòng từ ls khi nó xuất hiện, và chuyển dòng đó tới grep.


3
2017-08-23 22:24



Nitpick: trật tự các vấn đề trong chuyển hướng nếu bạn chuyển hướng một fd khác: echo yes 1>&2 2>/tmp/blah; wc -l /tmp/blah; echo yes 2>/tmp/blah 1>&2; wc -l /tmp/blah Hơn nữa, chuyển hướng đến một tập tin sẽ chỉ sử dụng chuyển hướng cuối cùng. echo yes >/tmp/blah >/tmp/blah2 sẽ chỉ viết thư cho /tmp/blah2. - muru
Chuyển hướng không thực sự là đối số cho chương trình. Chương trình sẽ không biết hoặc quan tâm nơi đầu ra của nó đi (hoặc đầu vào đến từ đâu). Nó chỉ là cách nói bash làm thế nào để sắp xếp mọi thứ trước khi chạy chương trình. - Alois Mahdal


Lưu ý: Câu trả lời phản ánh sự hiểu biết của riêng tôi về các cơ chế này được cập nhật, tích luỹ qua nghiên cứu và đọc câu trả lời của các đồng nghiệp trên trang web này và unix.stackexchange.comvà sẽ được cập nhật khi thời gian trôi qua. Đừng ngần ngại đặt câu hỏi hoặc đề xuất cải tiến trong các ý kiến. Tôi cũng đề nghị bạn thử xem các syscalls hoạt động như thế nào trong shell strace chỉ huy. Ngoài ra xin đừng bị đe dọa bởi khái niệm nội bộ hoặc syscalls - bạn không cần phải biết hoặc có thể sử dụng chúng để hiểu cách thức hoạt động của trình bao, nhưng chúng chắc chắn giúp hiểu được.

TL; DR

  • | đường ống không liên kết với một mục nhập trên đĩa, do đó không có inode số lượng hệ thống tệp đĩa (nhưng có inode trong pipefs hệ thống tệp ảo trong không gian hạt nhân), nhưng các chuyển hướng thường liên quan đến các tệp, có các mục đĩa và do đó có inode tương ứng.
  • ống không lseek()'các lệnh có thể như vậy không thể đọc một số dữ liệu và sau đó tua lại, nhưng khi bạn chuyển hướng > hoặc là < thường là một tập tin lseek() đối tượng có thể, do đó, lệnh có thể điều hướng tuy nhiên chúng vui lòng.
  • chuyển hướng là thao tác trên các bộ mô tả tập tin, có thể là nhiều; ống chỉ có hai bộ mô tả tập tin - một cho lệnh trái và một cho lệnh đúng
  • chuyển hướng trên các luồng và đường ống tiêu chuẩn đều được đệm.
  • đường ống hầu như luôn luôn liên quan đến việc đánh thức, chuyển hướng - không phải luôn luôn
  • các đường ống luôn xử lý các bộ mô tả tập tin, chuyển hướng - hoặc sử dụng các tệp thực sự với tên tệp trên đĩa hoặc các bộ mô tả tệp.
  • là các phương thức truyền thông giữa các quá trình, trong khi các chuyển hướng chỉ là các thao tác trên các tệp mở hoặc các đối tượng giống như tệp
  • cả hai đều sử dụng dup2() syscalls bên dưới mui xe để cung cấp bản sao của các bộ mô tả tập tin, nơi mà dòng chảy dữ liệu thực tế xảy ra.
  • chuyển hướng có thể được áp dụng "toàn cầu" với exec được xây dựng trong lệnh (xem điều này và điều này ), vì vậy nếu bạn làm exec > output.txt mọi lệnh sẽ ghi vào output.txt từ đó trở đi. | các đường ống chỉ được áp dụng cho lệnh hiện tại (có nghĩa là lệnh đơn giản hoặc subshell như seq 5 | (head -n1; head -n2) hoặc các lệnh ghép.
  • Khi chuyển hướng được thực hiện trên các tệp, những thứ như echo "TEST" > file và echo "TEST" >> file cả hai sử dụng open() syscall trên tập tin đó (Xem thêm) và nhận được mô tả tập tin từ nó để chuyển nó đến dup2(). Ống | chỉ sử dụng pipe() và dup2() syscall.

Giới thiệu

Để hiểu hai cơ chế này khác nhau như thế nào, cần phải hiểu các thuộc tính thiết yếu của chúng, lịch sử đằng sau hai, và nguồn gốc của chúng trong ngôn ngữ lập trình C. Trong thực tế, biết những gì mô tả tập tin được, và làm thế nào dup2() và pipe() các cuộc gọi hệ thống hoạt động là rất cần thiết, cũng như lseek(). Shell có nghĩa là một cách để làm cho các cơ chế này trừu tượng với người dùng, nhưng việc đào sâu hơn trừu tượng sẽ giúp hiểu được bản chất thực sự của hành vi của shell.

Nguồn gốc của chuyển hướng và đường ống

Theo bài báo của Dennis Ritche Prorogetic Petroglyphs, ống có nguồn gốc từ một 1964 bản ghi nhớ nội bộ bởi Malcolm Douglas McIlroy, vào thời điểm họ đang làm việc Hệ điều hành Multics. Trích dẫn:

Để đặt mối quan tâm mạnh nhất của tôi vào một nutshell:

  1. Chúng ta nên có một số cách kết nối các chương trình như vòi vườn - vít trong phân đoạn khác khi nó trở thành khi nó trở nên cần thiết để massage dữ liệu theo một cách khác. Đây cũng là cách của IO.

Rõ ràng là tại thời điểm các chương trình có khả năng ghi vào đĩa, tuy nhiên điều đó không hiệu quả nếu đầu ra lớn. Để trích dẫn lời giải thích của Brian Kernighan trong Đường ống Unix video :

Đầu tiên, bạn không cần phải viết một chương trình lớn - bạn đã có các chương trình nhỏ hơn có thể đã làm các phần của công việc ... Khác là có thể số lượng dữ liệu bạn đang xử lý sẽ không phù hợp nếu bạn đã lưu nó trong một tập tin ... bởi vì hãy nhớ, chúng ta sẽ quay trở lại những ngày khi đĩa trên những thứ này có, nếu bạn may mắn, một Megabyte hoặc hai dữ liệu ... Vì vậy, đường ống không bao giờ phải khởi tạo toàn bộ đầu ra .

Do đó sự khác biệt về khái niệm là rõ ràng: ống là cơ chế làm cho các chương trình nói chuyện với nhau. Chuyển hướng - là cách viết để gửi ở cấp cơ bản. Trong cả hai trường hợp, vỏ làm cho hai thứ này trở nên dễ dàng, nhưng bên dưới mui xe, có rất nhiều thứ đang diễn ra.

Đi sâu hơn: syscalls và nội bộ hoạt động của vỏ

Chúng ta bắt đầu với khái niệm về -bộ mô tả tập tin. Mô tả tệp mô tả về cơ bản một tệp mở (cho dù đó là tệp trên đĩa hoặc trong bộ nhớ hoặc tệp ẩn danh), được biểu thị bằng số nguyên. Cả hai luồng dữ liệu chuẩn  (stdin, stdout, stderr) là các bộ mô tả tập tin 0,1 và 2 tương ứng. Họ đến từ đâu ? Vâng, trong các lệnh shell, các bộ mô tả tập tin được thừa hưởng từ bố mẹ - shell của chúng. Và đó là sự thật nói chung cho tất cả các quy trình - quá trình con kế thừa các mô tả tệp của cha mẹ. Dành cho daemon nó là phổ biến để đóng tất cả các mô tả tập tin kế thừa và / hoặc chuyển hướng đến những nơi khác.

Quay lại chuyển hướng. Nó thực sự là gì? Đó là một cơ chế cho trình bao để chuẩn bị các bộ mô tả tập tin cho lệnh (vì các chuyển hướng được thực hiện bởi trình bao trước khi lệnh chạy), và trỏ chúng đến nơi người dùng gợi ý. Các tiêu chuẩn độ nét chuyển hướng đầu ra là

[n]>word

Cái đó [n] có số mô tả tập tin. Khi bạn làm echo "Something" > /dev/null số 1 ​​được ngụ ý ở đó, và echo 2> /dev/null.

Bên dưới mui xe, điều này được thực hiện bằng cách sao chép bộ mô tả tệp qua dup2() gọi hệ thống. Hãy lấy df > /dev/null. Hệ vỏ sẽ tạo ra một tiến trình con nơi df chạy, nhưng trước đó nó sẽ mở /dev/null dưới dạng bộ mô tả tệp # 3 và dup2(3,1) sẽ được phát hành, tạo bản sao của bộ mô tả tập tin 3 và bản sao sẽ là 1. Bạn biết cách bạn có hai tệp file1.txt và file2.txtvà khi bạn làm cp file1.txt file2.txt bạn sẽ có hai tệp giống nhau nhưng bạn có thể thao tác chúng một cách độc lập? Đó là kinda cùng một điều xảy ra ở đây. Thường thì bạn có thể thấy rằng trước khi chạy, bash sẽ làm dup(1,10) để tạo một bộ mô tả tập tin sao chép số 1 stdout (và bản sao đó sẽ là fd # 10) để khôi phục sau này. Điều quan trọng cần lưu ý là khi bạn xem xét các lệnh tích hợp (là một phần của chính vỏ và không có tệp nào trong /bin hoặc ở nơi khác) hoặc lệnh đơn giản trong shell không tương tác, shell không tạo ra một tiến trình con.

Và sau đó chúng tôi có những thứ như [n]>&[m] và [n]&<[m]. Điều này là nhân bản mô tả tập tin, mà cơ chế tương tự như dup2() chỉ bây giờ nó nằm trong cú pháp shell, thuận tiện cho người dùng.

Một trong những điều quan trọng cần lưu ý về chuyển hướng là thứ tự của chúng không cố định, nhưng rất quan trọng đối với cách trình diễn thông dịch người dùng muốn gì. So sánh như sau:

# Make copy of where fd 2 points , then redirect fd 2
$ ls -l /proc/self/fd/  3>&2  2> /dev/null
total 0
lrwx------ 1 user user 64 Sep 13 00:08 0 -> /dev/pts/0
lrwx------ 1 user user 64 Sep 13 00:08 1 -> /dev/pts/0
l-wx------ 1 user user 64 Sep 13 00:08 2 -> /dev/null
lrwx------ 1 runner user 64 Sep 13 00:08 3 -> /dev/pts/0
lr-x------ 1 user user 64 Sep 13 00:08 4 -> /proc/29/fd

# redirect fd #2 first, then clone it
$ ls -l /proc/self/fd/    2> /dev/null 3>&2
total 0
lrwx------ 1 user user 64 Sep 13 00:08 0 -> /dev/pts/0
lrwx------ 1 user user 64 Sep 13 00:08 1 -> /dev/pts/0
l-wx------ 1 user user 64 Sep 13 00:08 2 -> /dev/null
l-wx------ 1 user user 64 Sep 13 00:08 3 -> /dev/null
lr-x------ 1 user user 64 Sep 13 00:08 4 -> /proc/31/fd

Việc sử dụng thực tế của chúng trong shell scripting có thể linh hoạt:

và nhiều thứ khác.

Hệ thống ống nước với pipe() và dup2()

Vậy làm thế nào để ống được tạo ra? Thông qua pipe() syscall, sẽ lấy làm đầu vào một mảng (aka list) được gọi là pipefd của hai mục loại int (số nguyên). Hai số nguyên này là các bộ mô tả tập tin. Các pipefd[0] sẽ là đầu đọc của ống và pipefd[1] sẽ là đầu ghi. Vì vậy, trong df | grep 'foo', grep sẽ nhận được bản sao của pipefd[0] và df sẽ nhận được một bản sao của pipefd[1]. Nhưng bằng cách nào ? Tất nhiên, với sự kỳ diệu của dup2() syscall. Dành cho df trong ví dụ của chúng ta, hãy nói pipefd[1] có # 4, vì vậy vỏ sẽ tạo ra một đứa trẻ, hãy làm dup2(4,1) (nhớ tôi cp ví dụ?), và sau đó làm execve() để thực sự chạy df. Một cách tự nhiên, df sẽ kế thừa tập tin mô tả # 1, nhưng sẽ không biết rằng nó không còn trỏ vào thiết bị đầu cuối, nhưng thực sự fd # 4, đó thực sự là đầu ghi của đường ống. Đương nhiên, điều tương tự sẽ xảy ra với grep 'foo' trừ các số mô tả tập tin khác nhau.

Bây giờ, câu hỏi thú vị: chúng ta có thể làm cho đường ống chuyển hướng fd # 2 là tốt, không chỉ fd # 1? Vâng, thực tế đó là những gì |& làm trong bash. Tiêu chuẩn POSIX yêu cầu ngôn ngữ lệnh shell để hỗ trợ df 2>&1 | grep 'foo' cú pháp cho mục đích đó, nhưng bash làm |& cũng.

Điều quan trọng cần lưu ý là các đường ống luôn đối phó với các bộ mô tả tập tin. Có tồn tại FIFO hoặc là tên ống, trong đó có một tên tập tin trên đĩa và cho phép bạn sử dụng nó như một tập tin, nhưng cư xử như một đường ống. Nhưng | các loại ống được gọi là ống ẩn danh - chúng không có tên tệp, bởi vì chúng thực sự chỉ là hai đối tượng được kết nối với nhau. Thực tế là chúng tôi không xử lý các tệp cũng có ý nghĩa quan trọng: đường ống không lseek()'có thể. Các tập tin, hoặc trong bộ nhớ hoặc trên đĩa, là tĩnh - các chương trình có thể sử dụng lseek() syscall để nhảy đến byte 120, sau đó quay lại byte 10, sau đó chuyển tiếp tất cả các cách để kết thúc. Các đường ống không tĩnh - chúng liên tục và do đó bạn không thể tua lại dữ liệu bạn nhận được từ chúng lseek(). Đây là những gì làm cho một số chương trình nhận thức được nếu họ đang đọc từ tập tin hoặc từ đường ống, và do đó họ có thể thực hiện các điều chỉnh cần thiết cho hiệu suất hiệu quả; nói cách khác, prog có thể phát hiện nếu tôi làm cat file.txt | prog hoặc là prog < input.txt. Ví dụ thực tế về điều đó là đuôi.

Hai thuộc tính rất thú vị khác của ống là chúng có bộ đệm, trên Linux là 4096 bytevà họ thực sự có hệ thống tệp như được định nghĩa trong mã nguồn Linux ! Chúng không chỉ đơn giản là một đối tượng để truyền dữ liệu xung quanh, chúng là một cơ sở hạ tầng! Thực tế, bởi vì có hệ thống tập tin pipefs, quản lý cả hai đường ống và FIFO, ống có inode số trên hệ thống tệp tương ứng của chúng:

# Stdout of ls is wired to pipe
$ ls -l /proc/self/fd/  | cat  
lrwx------ 1 user user 64 Sep 13 00:02 0 -> /dev/pts/0
l-wx------ 1 user user 64 Sep 13 00:02 1 -> pipe:[15655630]
lrwx------ 1 user user 64 Sep 13 00:02 2 -> /dev/pts/0
lr-x------ 1 user user 64 Sep 13 00:02 3 -> /proc/22/fd
# stdin of ls is wired to pipe
$ true | ls -l /proc/self/fd/0
lr-x------ 1 user user 64 Sep 13 03:58 /proc/self/fd/0 -> 'pipe:[54741]'

Trên các ống Linux là uni-directional, giống như chuyển hướng. Trên một số triển khai giống Unix, có các ống hai hướng. Mặc dù với ma thuật của kịch bản shell, bạn có thể thực hiện ống hai hướng trên Linux cũng.

Xem thêm:


3
2017-09-12 09:26





Để thêm vào các câu trả lời khác, cũng có sự khác biệt ngữ nghĩa tinh tế - ví dụ: ống gần dễ dàng hơn chuyển hướng:

seq 5 | (head -n1; head -n1)                # just 1
seq 5 > tmp5; (head -n1; head -n1) < tmp5   # 1 and 2
seq 5 | (read LINE; echo $LINE; head -n1)   # 1 and 2

Trong ví dụ đầu tiên, khi cuộc gọi đầu tiên đến head kết thúc, nó đóng ống và seq chấm dứt, vì vậy không có đầu vào nào cho thứ hai head.

Trong ví dụ thứ hai, đầu tiêu thụ dòng đầu tiên, nhưng khi nó đóng riêng stdin  ống, tệp vẫn mở cho cuộc gọi tiếp theo để sử dụng.

Ví dụ thứ ba cho thấy rằng nếu chúng ta sử dụng read để tránh đóng đường ống, nó vẫn có sẵn trong tiến trình con.

Vì vậy, "luồng" là thứ mà chúng tôi chuyển dữ liệu qua (stdin, v.v) và giống nhau trong cả hai trường hợp, nhưng đường ống kết nối luồng từ hai quy trình, trong đó chuyển hướng kết nối luồng giữa quá trình và tệp, do đó bạn có thể thấy nguồn gốc của cả hai điểm tương đồng và khác biệt.

P.S. Nếu bạn tò mò và / hoặc ngạc nhiên bởi những ví dụ như tôi, bạn có thể đào sâu hơn bằng cách sử dụng trap để xem các quy trình giải quyết như thế nào, ví dụ:

(trap 'echo seq EXITed >&2' EXIT; seq 5) | (trap 'echo all done' EXIT; (trap 'echo first head exited' EXIT; head -n1)
echo '.'
(trap 'echo second head exited' EXIT; head -n1))

Đôi khi quá trình đầu tiên đóng trước 1 được in, đôi khi sau đó.

Tôi cũng thấy thú vị khi sử dụng exec <&- để đóng luồng từ chuyển hướng để ước tính hành vi của đường ống (mặc dù có lỗi):

seq 5 > tmp5
(trap 'echo all done' EXIT
(trap 'echo first head exited' EXIT; head -n1)
echo '.'
exec <&-
(trap 'echo second head exited' EXIT; head -n1)) < tmp5`

2
2018-06-05 00:54



"khi cuộc gọi đầu tiên kết thúc, nó đóng ống" Điều này thực sự không chính xác vì hai lý do. Một, (head -n1; head -n1) là subshell với hai lệnh, mỗi câu lệnh kế thừa kết thúc đọc của đường ống là mô tả 0, và do đó subshell AND mỗi lệnh có bộ mô tả tệp đó mở. Lý do thứ hai, bạn có thể thấy rằng với strace -f bash -c 'seq 5 | (đầu -n1; đầu -n1) '. Vì vậy, đầu tiên chỉ đóng bản sao của bộ mô tả tập tin - Sergiy Kolodyazhnyy
Ví dụ thứ ba cũng không chính xác, bởi vì read chỉ tiêu thụ dòng đầu tiên (đó là một byte cho 1 và dòng mới). seq được gửi trong tổng số 10 byte (5 số và 5 dòng mới). Vì vậy, có 8 byte còn lại trong bộ đệm ống, và đó là lý do thứ hai head hoạt động - có dữ liệu vẫn có sẵn trong bộ đệm ống. Btw, đầu thoát chỉ khi có 0 byte đọc, kinda như trong head /dev/null - Sergiy Kolodyazhnyy
Cảm ơn bạn đã làm rõ. Tôi có hiểu chính xác rằng trong seq 5 | (head -n1; head -n1) cuộc gọi đầu tiên làm trống đường ống, do đó, nó vẫn tồn tại ở trạng thái mở nhưng không có dữ liệu cho cuộc gọi thứ hai đến head? Vì vậy, sự khác biệt trong hành vi giữa các đường ống và chuyển hướng là bởi vì đầu kéo tất cả các dữ liệu ra khỏi đường ống, nhưng chỉ có 2 dòng ra khỏi xử lý tập tin? - Julian de Bhal
Đúng rồi. Và đó là thứ có thể được nhìn thấy strace lệnh tôi đưa ra trong bình luận đầu tiên. Với chuyển hướng, tập tin tmp là trên đĩa mà làm cho nó có thể tìm kiếm (vì họ sử dụng lseek() syscall - lệnh có thể nhảy xung quanh tập tin từ byte đầu tiên đến cuối cùng tuy nhiên họ muốn. Nhưng đường ống là tuần tự và không thể tìm kiếm được. Vì vậy, cách duy nhất để đầu thực hiện công việc của mình là đọc mọi thứ trước tiên hoặc nếu tệp lớn - hãy ánh xạ một số thông tin đó đến RAM qua mmap() gọi điện. Tôi đã từng làm tail trong Python, và chạy vào cùng một vấn đề. - Sergiy Kolodyazhnyy
Nó cũng quan trọng cần nhớ rằng kết thúc đọc của đường ống (mô tả tập tin) được trao cho subshell đầu tiên (...)và subshell sẽ tạo bản sao stdin riêng của nó cho mỗi lệnh bên trong (...). Vì vậy, chúng được đọc về mặt kỹ thuật từ cùng một đối tượng. Đầu tiên head  nghĩ rằng nó đọc từ stdin riêng của nó. Thứ hai head nghĩ rằng nó có stdin riêng. Nhưng trong thực tế fd # 1 (stdin) của họ chỉ là bản sao của cùng một fd, được đọc cuối của đường ống. Ngoài ra, tôi đã đăng một câu trả lời, vì vậy có lẽ nó sẽ giúp làm rõ mọi thứ. - Sergiy Kolodyazhnyy


Tôi đã gặp vấn đề với điều này trong C ngày hôm nay. Về cơ bản, ống có các ngữ nghĩa khác nhau để chuyển hướng, ngay cả khi được gửi đến stdin. Thực sự tôi nghĩ rằng với sự khác biệt, đường ống nên đi đâu đó khác hơn stdin, để stdin và cho phép gọi nó stdpipe (để thực hiện một sự khác biệt tùy ý) có thể được xử lý theo nhiều cách khác nhau.

Xem xét điều này. Khi tạo một đầu ra chương trình cho một chương trình khác fstat dường như trở về số không như là st_size mặc dù ls -lha /proc/{PID}/fd cho thấy rằng có một tập tin. Khi chuyển hướng một tệp, đây không phải là trường hợp (ít nhất là trên debian wheezy, stretch và jessie vani và ubuntu 14.04, 16.04 vanilla.

nếu bạn cat /proc/{PID}/fd/0 với một chuyển hướng, bạn sẽ có thể lặp lại để đọc bao nhiêu lần tùy thích. Nếu bạn làm điều này với một đường ống bạn sẽ nhận thấy rằng lần thứ hai bạn chạy nhiệm vụ liên tiếp, bạn không nhận được cùng một đầu ra.


1
2017-10-26 16:17