Nhầm lẫn về stdin, stdout và stderr?


230

Tôi khá bối rối với mục đích của ba tập tin này. Nếu sự hiểu biết của tôi là chính xác, stdinthì tệp mà chương trình ghi vào các yêu cầu của nó để chạy một tác vụ trong quy trình, stdoutlà tệp mà hạt nhân ghi đầu ra của nó và quá trình yêu cầu nó truy cập thông tin từ đó, và stderrlà tệp vào mà tất cả các ngoại lệ được nhập vào. Khi mở các tệp này để kiểm tra xem những điều này có thực sự xảy ra hay không, tôi thấy dường như không có gì gợi ý như vậy!

Những gì tôi muốn biết là chính xác mục đích của các tệp này là gì, hoàn toàn không có câu trả lời với rất ít thuật ngữ công nghệ!


36
Quan sát: Câu hỏi này đã được chấp nhận trở lại vào năm 2010, nhưng ngày nay sẽ bị hạ cấp rất nhanh.
byxor

3
@Brandon Bạn có thể cung cấp một lý do? Tôi nghĩ rằng nó sẽ có giá trị cho nhận xét của bạn.
Độc lập

3
@byxor để công bằng, tôi sẽ hỏi: Bài đăng của op có yêu cầu mọi người giúp anh ta gỡ lỗi mã của mình không? có vẻ như Shouvik đã hỏi một câu hỏi liên quan đến mục đích của stdin, stdout và stderr. Bài viết của op dường như hết tò mò, phải không? (tôi thực sự đang tìm hiểu về điều này bản thân mình. Cảm ơn, vì đã không xóa bài đăng này)
sansae

2
@ user123456 bạn đúng. Tôi đã học để trở thành một nhà phát triển phần mềm và sau đó S / O là một nơi tuyệt vời để học về lập trình. Ban đầu chúng tôi dự định nó sẽ là một dịch vụ wiki sắp xếp cho tất cả các câu hỏi về khoa học máy tính. #juniorDevForLife
Shouvik

3
@Shouvik cảm ơn vì chút lịch sử đó. Tôi cũng đang học cách trở thành một nhà phát triển phần mềm (vừa được chấp nhận vào một trại mát mẻ trong sf). Tôi vẫn còn khá mới đối với S / O và vẫn không chắc chắn về những gì tôi có thể và không thể đăng. Tôi thấy rằng sự điều độ ở đây có thể khá nghiêm ngặt. Tôi thích thẻ băm đó. #juniorDevForLife. Tôi sẽ chiều bạn thay vì bình luận ở đây vì điều này không thêm gì vào cuộc thảo luận, nhưng tôi không tin S / O có hệ thống chiều. Có một ngày tuyệt vời.
sansae

Câu trả lời:


251

Đầu vào tiêu chuẩn - đây là xử lý tập tin mà quy trình của bạn đọc để lấy thông tin từ bạn.

Đầu ra tiêu chuẩn - quy trình của bạn ghi thông tin bình thường vào tệp xử lý này.

Lỗi tiêu chuẩn - quy trình của bạn ghi thông tin lỗi vào tệp xử lý này.

Điều đó thật tồi tệ khi tôi có thể làm được :-)

Tất nhiên, đó chủ yếu là theo quy ước. Không có gì ngăn bạn viết thông tin lỗi của bạn vào đầu ra tiêu chuẩn nếu bạn muốn. Bạn thậm chí có thể đóng hoàn toàn ba tay cầm tệp và mở tệp của riêng bạn cho I / O.

Khi quy trình của bạn bắt đầu, nó sẽ có các tay cầm này mở và nó chỉ có thể đọc từ và / hoặc viết cho chúng.

Theo mặc định, chúng có thể được kết nối với thiết bị đầu cuối của bạn (ví dụ: /dev/tty :) nhưng shell sẽ cho phép bạn thiết lập kết nối giữa các tay cầm này và các tệp và / hoặc thiết bị cụ thể (hoặc thậm chí là đường ống đến các quy trình khác) trước khi quá trình của bạn bắt đầu (một số các thao tác có thể là khá thông minh).

Một ví dụ là:

my_prog <inputfile 2>errorfile | grep XYZ

cái nào sẽ:

  • tạo một quy trình cho my_prog.
  • mở inputfilelàm đầu vào tiêu chuẩn của bạn (xử lý tệp 0).
  • mở errorfilenhư lỗi tiêu chuẩn của bạn (xử lý tệp 2).
  • tạo một quá trình khác cho grep.
  • đính kèm đầu ra my_progtiêu chuẩn của đầu vào tiêu chuẩn của grep.

Nhận xét của bạn:

Khi tôi mở các tệp này trong thư mục / dev, tại sao tôi không bao giờ thấy đầu ra của một tiến trình đang chạy?

Đó là bởi vì chúng không phải là các tập tin bình thường. Mặc dù UNIX trình bày mọi thứ dưới dạng một tệp trong một hệ thống tệp ở đâu đó, nhưng điều đó không làm cho nó ở mức thấp nhất. Hầu hết các tệp trong /devhệ thống phân cấp là ký tự hoặc khối thiết bị, thực sự là trình điều khiển thiết bị. Chúng không có kích thước nhưng chúng có số thiết bị chính và phụ.

Khi bạn mở chúng, bạn đã kết nối với trình điều khiển thiết bị chứ không phải tệp vật lý và trình điều khiển thiết bị đủ thông minh để biết rằng các quy trình riêng biệt nên được xử lý riêng.

Điều này cũng đúng với /prochệ thống tập tin Linux . Đó không phải là các tập tin thực, chỉ là các cổng kiểm soát chặt chẽ thông tin kernel.


1
Đó là phản ứng của bạn. Mặc dù tôi có thể hiểu mục đích của các tệp từ những gì bạn mô tả, tôi muốn chuyển một cấp độ nhiều hơn. Khi tôi mở các tệp này trong thư mục / dev, tại sao tôi không bao giờ thấy đầu ra của một tiến trình đang chạy. Giả sử tôi thực thi hàng đầu trên thiết bị đầu cuối, không nên xuất kết quả của nó vào tệp xuất chuẩn theo định kỳ, do đó khi nó được cập nhật, tôi có thể thấy một phiên bản đầu ra được in trên tệp này. Nhưng điều này không phải vậy .. Những tệp này không giống nhau (những tệp trong thư mục / dev).
Shouvik

7
Bởi vì những thứ đó không phải là tập tin kỹ thuật. Chúng là các nút thiết bị, cho biết một thiết bị cụ thể để ghi vào. UNIX có thể trình bày mọi thứ cho bạn dưới dạng trừu tượng tập tin nhưng điều đó không làm cho nó ở mức sâu nhất.
paxdiablo

1
Sử dụng khả năng chuyển hướng vỏ. xyz >xyz.outsẽ ghi đầu ra tiêu chuẩn của bạn vào một tệp vật lý có thể được đọc bởi các quy trình khác. xyz | grep somethingsẽ kết nối xyzthiết bị xuất chuẩn để grepstdin trực tiếp hơn. Nếu bạn muốn truy cập không bị chặn vào một quy trình mà bạn không kiểm soát theo cách đó, bạn sẽ cần xem xét một cái gì đó giống như /prochoặc viết mã để lọc đầu ra bằng cách móc vào kernel bằng cách nào đó. Có thể có các giải pháp khác nhưng tất cả chúng đều nguy hiểm như nhau :-)
paxdiablo

20
@Shouvik, lưu ý rằng đó /dev/stdinlà một liên kết tượng trưng đến /proc/self/fd/0- bộ mô tả tệp đầu tiên mà chương trình hiện đang chạy đã mở. Vì vậy, những gì được chỉ ra bởi /dev/stdinsẽ thay đổi từ chương trình này sang chương trình khác, bởi vì /proc/self/luôn luôn trỏ đến 'chương trình hiện đang chạy'. (Bất kỳ chương trình nào đang thực hiện opencuộc gọi.) /dev/stdinVà bạn bè đã được đặt ở đó để làm cho các tập lệnh shell setuid an toàn hơn và cho phép bạn chuyển tên tệp /dev/stdincho các chương trình chỉ hoạt động với các tệp, nhưng bạn muốn kiểm soát tương tác nhiều hơn. (Một ngày nào đó đây sẽ là một mẹo hữu ích để bạn biết. :)
sarnold

1
@ CarlosW.Mercado, một tệp là biểu hiện vật lý của dữ liệu. Ví dụ, các bit được lưu trữ trên đĩa cứng. Một xử lý tệp là (thường) một mã thông báo nhỏ được sử dụng để tham chiếu đến tệp đó, khi bạn đã mở nó.
paxdiablo

62

Nó sẽ là chính xác hơn để nói rằng stdin, stdoutstderrlà "I / O suối" chứ không phải là tập tin. Như bạn đã nhận thấy, những thực thể này không sống trong hệ thống tập tin. Nhưng triết lý Unix, theo như I / O có liên quan, là "mọi thứ đều là một tập tin". Trong thực tế, điều đó thực sự có nghĩa là bạn có thể sử dụng các chức năng cùng một thư viện và các giao diện ( printf, scanf, read, write,select , vv) mà không lo lắng về việc liệu các dòng I / O được kết nối với một bàn phím, một tập tin trên đĩa, một ổ cắm, một đường ống, hoặc một số trừu tượng I / O khác.

Hầu hết các chương trình cần phải đọc đầu vào, đầu ra ghi, và các lỗi đăng nhập, vì vậy stdin, stdoutstderrđược xác định trước cho bạn, như là một tiện nghi lập trình. Đây chỉ là một quy ước và không được hệ điều hành thi hành.


Cảm ơn cho đầu vào của bạn. Bạn có biết làm thế nào tôi có thể chặn luồng dữ liệu đầu ra của một quá trình và xuất nó thành một tệp của riêng tôi không?
Shouvik

51

Để bổ sung cho các câu trả lời ở trên, đây là tổng hợp về Chuyển hướng: Áo chuyển hướng

EDIT: Đồ họa này không hoàn toàn chính xác nhưng tôi không chắc tại sao ...

Đồ họa cho biết 2> & 1 có tác dụng tương tự như &> tuy nhiên

ls Documents ABC > dirlist 2>&1
#does not give the same output as 
ls Documents ABC > dirlist &>

4
Nhận xét của bạn kết hợp với câu trả lời được chấp nhận có ý nghĩa hoàn hảo và giải thích rõ ràng mọi thứ! Cảm ơn!
Mykola

1
Một bưc tranh đang gia ngan lơi noi !
tauseef_CperedGuy

22

Tôi sợ sự hiểu biết của bạn là hoàn toàn lạc hậu. :)

Hãy nghĩ về "tiêu chuẩn trong", "tiêu chuẩn ra" và "lỗi tiêu chuẩn" từ quan điểm của chương trình , không phải từ quan điểm của hạt nhân.

Khi một chương trình cần in đầu ra, nó thường in thành "tiêu chuẩn ra". Một chương trình thường in đầu ra ra theo tiêu chuẩn printf, in ra CHỈ theo tiêu chuẩn.

Khi một chương trình cần in thông tin lỗi (không nhất thiết là ngoại lệ, đó là cấu trúc ngôn ngữ lập trình, được áp đặt ở mức cao hơn nhiều), nó thường in thành "lỗi tiêu chuẩn". Nó thường làm như vậy với fprintf, chấp nhận một luồng tệp để sử dụng khi in. Luồng tệp có thể là bất kỳ tệp nào được mở để ghi: lỗi tiêu chuẩn, lỗi tiêu chuẩn hoặc bất kỳ tệp nào khác đã được mở bằng fopenhoặc fdopen.

"Standard in" được sử dụng khi tệp cần đọc đầu vào, sử dụng freadhoặc fgets, hoặc getchar.

Bất kỳ tệp nào trong số này có thể dễ dàng được chuyển hướng từ trình bao, như thế này:

cat /etc/passwd > /tmp/out     # redirect cat's standard out to /tmp/foo
cat /nonexistant 2> /tmp/err   # redirect cat's standard error to /tmp/error
cat < /etc/passwd              # redirect cat's standard input to /etc/passwd

Hoặc, toàn bộ enchilada:

cat < /etc/passwd > /tmp/out 2> /tmp/err

Có hai cảnh báo quan trọng: Thứ nhất, "tiêu chuẩn trong", "tiêu chuẩn ra" và "lỗi tiêu chuẩn" chỉ là một quy ước. Chúng là một quy ước rất mạnh , nhưng tất cả chỉ là một thỏa thuận rằng thật tuyệt khi có thể chạy các chương trình như thế này:grep echo /etc/services | awk '{print $2;}' | sort và có đầu ra tiêu chuẩn của mỗi chương trình được nối vào đầu vào tiêu chuẩn của chương trình tiếp theo.

Thứ hai, tôi đã đưa cho các chức năng ISO C tiêu chuẩn để làm việc với các dòng tập tin ( FILE *đối tượng) - ở cấp hạt nhân, đó là tất cả các file descriptor ( inttham chiếu đến bảng tập tin) và nhiều hoạt động thấp cấp như readwrite, mà không làm bộ đệm hạnh phúc của các chức năng ISO C. Tôi đã tìm cách giữ cho nó đơn giản và sử dụng các chức năng dễ dàng hơn, nhưng tôi nghĩ tất cả giống như bạn nên biết các lựa chọn thay thế. :)


Vì vậy, khi quá trình đang được thực thi, nó ghi lỗi vào tệp stderr này hoặc khi chương trình đang được biên dịch từ nguồn của nó. Ngoài ra khi chúng ta nói về các tệp này từ góc độ của trình biên dịch thì nó có khác so với khi nó được so sánh với một chương trình không?
Shouvik

1
@Shouvik, trình biên dịch chỉ là một chương trình khác, với stdin, stdout và stderr của riêng nó. Khi trình biên dịch cần viết cảnh báo hoặc lỗi, nó sẽ ghi chúng vào stderr. Khi trình biên dịch đầu ra xuất mã trung gian cho trình biên dịch chương trình, nó có thể viết mã trung gian trên thiết bị xuất chuẩn và trình biên dịch có thể chấp nhận đầu vào của nó trên stdin, nhưng tất cả những gì sẽ ở phía sau từ góc nhìn của bạn với tư cách là người dùng.) một chương trình được biên dịch, chương trình đó cũng có thể ghi lỗi thành lỗi tiêu chuẩn của nó, nhưng nó không liên quan gì đến việc được biên dịch.
đăng

Cảm ơn mã thông tin đó. Tôi đoán rằng thật ngu ngốc khi tôi không nhìn thấy nó trong viễn cảnh đó ...: P
Shouvik

1
Vì vậy, bạn đang nói rằng tiêu chuẩn giúp chúng tôi in chương trình
babygame0ver

9

stdin

Đọc đầu vào thông qua bảng điều khiển (ví dụ: Bàn phím nhập). Được sử dụng trong C với scanf

scanf(<formatstring>,<pointer to storage> ...);

xuất sắc

Tạo đầu ra cho giao diện điều khiển. Được sử dụng trong C với printf

printf(<string>, <values to print> ...);

stderr

Tạo đầu ra 'lỗi' cho bàn điều khiển. Được sử dụng trong C với fprintf

fprintf(stderr, <string>, <values to print> ...);

Chuyển hướng

Nguồn cho stdin có thể được chuyển hướng. Ví dụ, thay vì đến từ đầu vào bàn phím, nó có thể đến từ một tệp ( echo < file.txt) hoặc chương trình khác ( ps | grep <userid>).

Các điểm đến cho thiết bị xuất chuẩn, thiết bị xuất chuẩn cũng có thể được chuyển hướng. Ví dụ, thiết bị xuất chuẩn có thể được chuyển hướng đến một tệp : ls . > ls-output.txt, trong trường hợp này, đầu ra được ghi vào tệp ls-output.txt. Stderr có thể được chuyển hướng với 2>.


8

Tôi nghĩ mọi người nói stderrchỉ nên sử dụng cho các thông báo lỗi là sai lệch.

Nó cũng nên được sử dụng cho các thông báo có ý nghĩa đối với người dùng đang chạy lệnh và không dành cho bất kỳ người tiêu dùng dữ liệu tiềm năng nào (ví dụ: nếu bạn chạy một ống vỏ có một số lệnh bạn không muốn có thông báo như "nhận mục 30 42424 "xuất hiện stdoutvì chúng sẽ gây nhầm lẫn cho người tiêu dùng, nhưng bạn vẫn có thể muốn người dùng nhìn thấy họ.

Xem điều này cho lý do lịch sử:

"Tất cả các chương trình đặt chẩn đoán trên đầu ra tiêu chuẩn. Điều này luôn gây ra sự cố khi đầu ra được chuyển hướng thành một, nhưng trở nên không thể chịu đựng được khi đầu ra được gửi đến một quy trình không nghi ngờ. Tuy nhiên, không muốn vi phạm tính đơn giản của đầu vào tiêu chuẩn- Mô hình đầu ra tiêu chuẩn, mọi người chấp nhận tình trạng này qua v6. Ngay sau đó Dennis Ritchie đã cắt nút Gordian bằng cách đưa ra lỗi tiêu chuẩn. Điều đó là không đủ. Với chẩn đoán đường ống có thể đến từ bất kỳ chương trình nào chạy cùng lúc. để nhận diện chính họ. "


3

Sử dụng ps -aux cho thấy các quy trình hiện tại, tất cả các quy trình được liệt kê trong / Proc / as / Proc / (pid) /, bằng cách gọi cat / Proc / (pid) / fd / 0, nó in bất cứ thứ gì tìm thấy trong đầu ra tiêu chuẩn của quá trình đó tôi nghĩ Đương nhiên rồi,

/ Proc / (pid) / fd / 0 - Tệp đầu ra tiêu chuẩn
/ Proc / (pid) / fd / 1 - Tệp đầu vào tiêu chuẩn
/ Proc / (pid) / fd / 2 - Tệp lỗi tiêu chuẩn

ví dụcửa sổ đầu cuối của tôi

Nhưng chỉ hoạt động tốt cho / bin / bash, các quy trình khác thường không có gì trong 0 nhưng nhiều lỗi được viết bằng 2


3

Để biết thông tin có thẩm quyền về các tệp này, hãy kiểm tra các trang man, chạy lệnh trên thiết bị đầu cuối của bạn.

$ man stdout 

Nhưng đối với một câu trả lời đơn giản, mỗi tệp dành cho:

stdout cho một luồng ra

stdin cho đầu vào luồng

stderr để in lỗi hoặc thông điệp tường trình.

Mỗi chương trình unix có mỗi một trong những luồng đó.


2

stderr sẽ không thực hiện bộ đệm IO Cache, vì vậy nếu ứng dụng của chúng tôi cần in thông tin tin nhắn quan trọng (một số lỗi, ngoại lệ) lên bàn điều khiển hoặc để sử dụng nó khi sử dụng stdout để in thông tin nhật ký chung vì nó sử dụng bộ đệm IO Cache có thể xảy ra trước khi viết tin nhắn của chúng tôi vào ứng dụng tập tin có thể đóng, để lại việc gỡ lỗi phức tạp


0

Một tệp có bộ đệm liên quan được gọi là luồng và được khai báo là con trỏ tới loại FILE đã xác định. Hàm fopen () tạo dữ liệu mô tả nhất định cho luồng và trả về một con trỏ để chỉ định luồng trong tất cả các giao dịch tiếp theo. Thông thường có ba luồng mở với các con trỏ không đổi được khai báo trong tiêu đề và được liên kết với các tệp mở tiêu chuẩn. Khi khởi động chương trình, ba luồng được xác định trước và không cần phải mở một cách rõ ràng: đầu vào tiêu chuẩn (để đọc đầu vào thông thường), đầu ra tiêu chuẩn (để ghi đầu ra thông thường) và lỗi tiêu chuẩn (để ghi đầu ra chẩn đoán). Khi mở luồng lỗi tiêu chuẩn không được đệm đầy đủ; các luồng đầu vào tiêu chuẩn và đầu ra tiêu chuẩn được đệm hoàn toàn khi và chỉ khi luồng có thể được xác định không tham chiếu đến một thiết bị tương tác

https://www.mkssoftware.com/docs/man5/stdio.5.asp

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.