Là> & - hiệu quả hơn> / dev / null?


58

Hôm qua tôi đã đọc bình luận SO này nói rằng trong vỏ (ít nhất bash) >&-"có kết quả tương tự như" >/dev/null.

Nhận xét đó thực sự đề cập đến hướng dẫn ABS là nguồn thông tin của nó. Nhưng nguồn đó nói rằng >&-cú pháp "đóng mô tả tập tin".

Tôi không rõ liệu hai hành động đóng bộ mô tả tệp và chuyển hướng nó đến thiết bị null có hoàn toàn tương đương hay không. Vì vậy, câu hỏi của tôi là: họ?

Trên bề mặt của nó có vẻ như việc đóng một mô tả giống như đóng một cánh cửa nhưng chuyển hướng nó đến một thiết bị null đang mở ra một cánh cửa để lấp lửng! Cả hai dường như không giống hệt tôi vì nếu tôi thấy một cánh cửa đóng kín, tôi sẽ không cố gắng ném bất cứ thứ gì ra khỏi nó, nhưng nếu tôi thấy một cánh cửa mở tôi sẽ cho rằng mình có thể.

Nói cách khác, tôi đã luôn tự hỏi liệu >/dev/nullcó nghĩa là điều đó cat mybigfile >/dev/nullthực sự sẽ xử lý từng byte của tệp và ghi nó vào /dev/nullđó mà quên nó đi. Mặt khác, nếu shell gặp một bộ mô tả tệp đã đóng, tôi có xu hướng nghĩ (nhưng không chắc chắn) rằng nó sẽ không viết bất cứ điều gì, mặc dù câu hỏi vẫn là liệu có catcòn đọc từng byte hay không.

Nhận xét này nói >&->/dev/null" nên " giống nhau, nhưng nó không phải là câu trả lời quá vang dội với tôi. Tôi muốn có một câu trả lời có thẩm quyền hơn với một số tham chiếu đến lõi tiêu chuẩn hoặc nguồn hay không ...


Nếu tôi muốn làm từ thiện, tôi sẽ nói rằng bình luận không có nghĩa là họ được thực hiện theo cùng một cách, chỉ là cả hai đều có cùng kết quả cuối cùng là ngăn bạn nhìn thấy đầu ra của chương trình.
Barmar

Câu trả lời:


71

Không, bạn chắc chắn không muốn đóng mô tả tệp 0, 1 và 2.

Nếu bạn làm như vậy, lần đầu tiên ứng dụng mở tệp, nó sẽ trở thành stdin / stdout / stderr ...

Chẳng hạn, nếu bạn làm:

echo text | tee file >&-

Khi tee(ít nhất là một số triển khai, như busybox ') mở tệp để ghi, nó sẽ được mở trên mô tả tệp 1 (stdout). Vì vậy, teesẽ viết texthai lần vào file:

$ echo text | strace tee file >&-
[...]
open("file", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 1
read(0, "text\n", 8193)                 = 5
write(1, "text\n", 5)                   = 5
write(1, "text\n", 5)                   = 5
read(0, "", 8193)                       = 0
exit_group(0)                           = ?

Điều đó đã được biết là gây ra lỗ hổng bảo mật. Ví dụ:

chsh 2>&-

chsh(một ứng dụng setuid) có thể kết thúc bằng cách viết thông báo lỗi /etc/passwd.

Một số công cụ và thậm chí một số thư viện cố gắng bảo vệ chống lại điều đó. Chẳng hạn, GNU teesẽ di chuyển bộ mô tả tệp sang một ở trên 2 nếu các tệp mà nó mở để ghi được gán 0, 1, 2 trong khi busybox teesẽ không.

Hầu hết các công cụ, nếu chúng không thể ghi vào thiết bị xuất chuẩn (vì chẳng hạn như nó không mở), sẽ báo cáo một thông báo lỗi trên stderr (theo ngôn ngữ của người dùng có nghĩa là xử lý thêm để mở và phân tích các tệp bản địa hóa ...), vì vậy nó sẽ kém hiệu quả hơn đáng kể và có thể khiến chương trình bị lỗi.

Trong mọi trường hợp, nó sẽ không hiệu quả hơn. Chương trình vẫn sẽ thực hiện một write()cuộc gọi hệ thống. Nó chỉ có thể hiệu quả hơn nếu chương trình từ bỏ việc ghi vào thiết bị xuất chuẩn / thiết bị xuất chuẩn sau lần write()gọi hệ thống thất bại đầu tiên , nhưng các chương trình thường không làm điều đó. Họ thường thoát hoặc có lỗi hoặc tiếp tục cố gắng.


4
Tôi nghĩ rằng câu trả lời này sẽ còn tốt hơn nếu đoạn cuối cùng ở trên cùng (vì đó là câu trả lời trực tiếp nhất cho câu hỏi của OP), và sau đó tiếp tục thảo luận tại sao đó là một ý tưởng tồi ngay cả khi nó hầu như không hoạt động. Nhưng tôi sẽ lấy nó, có một upvote. ;)
CVn

@ StéphaneChazelas: Như Michael đã nói, tôi đã mong đợi đoạn cuối cùng trên đầu, nhưng cảm ơn vì đã làm rõ nó thực sự có lẽ chỉ tạo ra vấn đề. Vì vậy, tôi đoán một câu hỏi phụ trợ sẽ là khi nào việc đóng FD thực sự hữu ích? Hay tôi nên hỏi nó như một câu hỏi riêng biệt?
jamadagni

@jamadagni, xem unix.stackexchange.com/search?q=user%3A22565+%223%3E%26-%22 để biết một số ví dụ
Stéphane Chazelas

1
@jamadagni Nếu liên kết được cung cấp bởi Stéphane không trả lời câu hỏi, tôi sẽ nói rằng âm thanh đó giống như sự khởi đầu của một câu hỏi riêng vì nó không liên quan trực tiếp đến hiệu quả tương đối của hai phương pháp.
CVn

1
Tôi đánh giá cao rằng Stephane bắt đầu với cảnh báo bảo mật quan trọng theo chiều dọc này, vì nó sẽ ít được nhìn thấy hơn nếu đoạn cuối cùng được đặt lên hàng đầu. +1 từ tôi.
Olivier Dulac

14

IOW Tôi đã luôn tự hỏi nếu >/dev/nullcó nghĩa là nó cat mybigfile >/dev/nullthực sự sẽ xử lý từng byte của tệp và ghi nó vào /dev/nullđó để quên nó.

Đây không phải là một câu trả lời đầy đủ cho câu hỏi của bạn, nhưng vâng, trên đây là cách nó hoạt động.

catđọc (các) tệp được đặt tên hoặc đầu vào tiêu chuẩn nếu không có tệp nào được đặt tên và xuất ra đầu ra tiêu chuẩn của nó cho đến khi gặp EOF trên (bao gồm cả đầu vào tiêu chuẩn) tệp cuối cùng được đặt tên. Đó là công việc của nó .

Bằng cách thêm >/dev/nullbạn đang chuyển hướng đầu ra tiêu chuẩn sang / dev / null. Đó là một tệp đặc biệt (một nút thiết bị) sẽ loại bỏ mọi thứ được ghi trong đó (và ngay lập tức trả về EOF khi đọc). Lưu ý rằng chuyển hướng I / O là một tính năng được cung cấp bởi shell chứ không phải bởi từng ứng dụng riêng lẻ và không có gì kỳ diệu về tên / dev / null, chỉ có những gì xảy ra ở đó trên hầu hết các hệ thống giống Unix .

Cũng cần lưu ý rằng các cơ chế cụ thể của các nút thiết bị khác nhau tùy theo hệ điều hành, nhưng cat (trong hệ thống GNU, có nghĩa là coreutils) là đa nền tảng (cùng mã nguồn cần chạy ít nhất trên Linux và Hurd) và do đó không thể có sự phụ thuộc vào các nhân hệ điều hành cụ thể. Ngoài ra, nó vẫn hoạt động nếu bạn tạo bí danh / dev / null (trên Linux, điều này có nghĩa là một nút thiết bị có cùng số thiết bị chính / phụ) với tên khác. Và luôn luôn có trường hợp viết ở một nơi khác hoạt động hiệu quả như nhau (giả sử, / dev / zero).

Nó theo sau mà catkhông biết về các thuộc tính đặc biệt của / dev / null và thực sự có khả năng là không biết về chuyển hướng ở vị trí đầu tiên, nhưng nó vẫn cần thực hiện chính xác cùng một công việc: nó đọc các tệp được đặt tên và xuất nội dung của / những tập tin đó đến đầu ra tiêu chuẩn của nó. Rằng đầu ra tiêu chuẩn catxảy ra để đi vào một khoảng trống không phải là điều mà catbản thân nó quan tâm.


2
Để mở rộng câu trả lời của bạn: Có, cat mybigfile > /dev/nullsẽ khiến catđọc từng byte bigfilevào bộ nhớ. Và, với mỗi nbyte, nó sẽ gọi write(1, buffer, n). Không biết đến catchương trình, ý writechí hoàn toàn không làm gì cả (ngoại trừ có thể đối với một số sổ sách tầm thường). Viết vào /dev/nullkhông yêu cầu xử lý mỗi byte.
G-Man nói 'Phục hồi Monica'

2
Tôi nhớ rằng tôi đã bị thổi bay khi đọc nguồn nhân Linux của thiết bị / dev / null. Tôi đã hy vọng sẽ có một số hệ thống bộ đệm miễn phí () ing công phu, v.v., nhưng, không, về cơ bản nó chỉ là một sự trở lại ().
Brian Minton

4
@ G-Man: Tôi không biết rằng bạn có thể đảm bảo điều đó là đúng trong mọi trường hợp. Bây giờ tôi không thể tìm thấy bằng chứng, nhưng tôi nhớ lại một số triển khai của một trong hai cathoặc cpsẽ hoạt động bằng cách đưa mmapcác khối lớn của tệp nguồn vào bộ nhớ, sau đó gọi write()vào vùng được ánh xạ. Nếu bạn đã ghi vào /dev/null, write()cuộc gọi sẽ trở lại ngay lập tức mà không bị lỗi trong các trang của tệp nguồn, vì vậy nó sẽ không bao giờ thực sự được đọc từ đĩa.
Nate Eldredge

2
Ngoài ra, một cái gì đó giống như GNU catchạy trên nhiều nền tảng, nhưng nhìn lướt qua mã nguồn sẽ hiển thị rất nhiều #ifdefs: đó không phải là cùng một mã chạy trên tất cả các nền tảng và có rất nhiều phần phụ thuộc vào hệ thống.
Nate Eldredge

@NateEldredge: Điểm thú vị, nhưng tôi chỉ dựa vào câu trả lời của Michael, vì vậy bạn không mâu thuẫn với tôi nhiều như bạn đang mâu thuẫn với Michael.
G-Man nói 'Phục hồi Monica'
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.