Là chuyển hướng với `>>` tương đương với`> `khi tệp đích chưa tồn tại?


80

Hãy xem xét một cái vỏ như Bash hoặc sh. Sự khác biệt cơ bản giữa >>>biểu hiện trong một trường hợp khi tệp đích tồn tại:

  • > cắt ngắn tập tin về kích thước không, sau đó ghi;
  • >> không cắt ngắn, nó ghi (nối thêm) vào cuối tập tin.

Nếu tập tin không tồn tại, nó được tạo với kích thước bằng không; sau đó viết cho. Điều này đúng cho cả hai nhà khai thác. Có vẻ như các toán tử tương đương khi tệp mục tiêu chưa tồn tại.

Họ có thật không?

Câu trả lời:


107

tl; dr

Số >>về cơ bản là "luôn luôn tìm cách kết thúc tập tin" trong khi >duy trì một con trỏ đến vị trí được viết cuối cùng.


Câu trả lời đầy đủ

(Lưu ý: tất cả các thử nghiệm của tôi được thực hiện trên Debian GNU / Linux 9).

Một sự khác biệt

Không, chúng không tương đương. Có một sự khác biệt khác. Nó có thể tự biểu hiện bất kể tập tin đích có tồn tại trước đó hay không.

Để quan sát nó, hãy chạy một quy trình tạo dữ liệu và chuyển hướng đến một tệp có >hoặc >>(ví dụ pv -L 10k /dev/urandom > blob). Hãy để nó chạy và thay đổi kích thước của tệp (ví dụ với truncate). Bạn sẽ thấy rằng >giữ cho bù (tăng trưởng) của nó trong khi >>luôn luôn nối đến cuối.

  • Nếu bạn cắt tệp thành kích thước nhỏ hơn (nó có thể là kích thước không)
    • >không quan tâm, nó sẽ viết ở phần bù mong muốn như không có gì xảy ra; ngay sau khi cắt bớt phần bù nằm ngoài phần cuối của tệp, điều này sẽ khiến tệp lấy lại kích thước cũ và phát triển hơn nữa, dữ liệu bị thiếu sẽ được lấp đầy bằng số không (theo cách thưa thớt, nếu có thể);
    • >> sẽ nối vào đầu mới, tệp sẽ phát triển từ kích thước cắt ngắn của nó.
  • Nếu bạn phóng to tập tin
    • >không quan tâm, nó sẽ viết ở phần bù mong muốn như không có gì xảy ra; ngay sau khi thay đổi kích thước, phần bù nằm ở đâu đó bên trong tệp, điều này sẽ khiến tệp ngừng phát triển trong một thời gian, cho đến khi phần bù đạt đến điểm cuối mới, thì tệp sẽ phát triển bình thường;
    • >> sẽ nối vào đầu mới, tập tin sẽ phát triển từ kích thước mở rộng của nó.

Một ví dụ khác là nối thêm (với một phần riêng >>) một cái gì đó bổ sung khi quá trình tạo dữ liệu đang chạy và ghi vào tệp. Điều này tương tự như mở rộng tập tin.

  • Quá trình tạo >sẽ ghi ở phần bù mong muốn và ghi đè lên dữ liệu bổ sung.
  • Quá trình tạo >>sẽ bỏ qua dữ liệu mới và nối thêm nó (điều kiện cuộc đua có thể xảy ra, hai luồng có thể bị xen kẽ, vẫn không có dữ liệu nào được ghi đè).

Thí dụ

Có vấn đề trong thực tế? Có câu hỏi này :

Tôi đang chạy một quá trình tạo ra nhiều đầu ra trên thiết bị xuất chuẩn. Gửi tất cả vào một tệp [...] Tôi có thể sử dụng một số loại chương trình xoay vòng nhật ký không?

Câu trả lời này cho biết giải pháp là logrotatevới copytruncatetùy chọn hoạt động như thế này:

Cắt bớt tệp nhật ký gốc tại chỗ sau khi tạo bản sao, thay vì di chuyển tệp nhật ký cũ và tùy ý tạo tệp nhật ký mới.

Theo những gì tôi đã viết ở trên, chuyển hướng với >sẽ làm cho nhật ký cắt ngắn trở nên lớn nhanh chóng. Độ thưa thớt sẽ tiết kiệm trong ngày, không có không gian đĩa đáng kể nên bị lãng phí. Tuy nhiên, mỗi bản ghi liên tiếp sẽ có ngày càng nhiều số 0 đứng đầu trong đó là hoàn toàn không cần thiết.

Nhưng nếu logrotatetạo các bản sao mà không giữ được độ thưa thớt, các số 0 đứng đầu này sẽ cần nhiều không gian đĩa hơn mỗi khi bản sao được tạo. Tôi chưa điều tra hành vi của công cụ, nó có thể đủ thông minh với độ thưa hoặc nén khi đang di chuyển (nếu bật tính năng nén). Tuy nhiên, các số không chỉ có thể gây rắc rối hoặc trung lập; không có gì tốt trong họ.

Trong trường hợp này, sử dụng >>thay vì >tốt hơn đáng kể, ngay cả khi tệp mục tiêu sắp được tạo.


Hiệu suất

Như chúng ta có thể thấy, hai toán tử hành động khác nhau không chỉ khi chúng bắt đầu mà còn sau đó. Điều này có thể gây ra một số khác biệt (tinh tế?) Hiệu suất. Hiện tại tôi không có kết quả kiểm tra có ý nghĩa để hỗ trợ hoặc từ chối nó, nhưng tôi nghĩ bạn không nên tự động cho rằng hiệu suất của chúng là như nhau nói chung.


9
Vì vậy, >>về cơ bản là "luôn luôn tìm cách kết thúc tập tin" trong khi >duy trì một con trỏ đến vị trí được viết cuối cùng. Có vẻ như có thể có một số khác biệt hiệu suất tinh tế trong cách họ làm việc là tốt ...
Mokubai

10
Trên cấp độ cuộc gọi hệ thống, >>sử dụng O_APPENDcờ đểopen() . Và thực tế, >sử dụng O_TRUNC, trong khi >>không. Sự kết hợp của O_TRUNC | O_APPENDcũng sẽ có thể, ngôn ngữ shell chỉ không cung cấp tính năng đó.
ilkkachu

3
@jjmontes, nguồn tiêu chuẩn sẽ là POSIX: pubs.opengroup.org/onlinepubs/9699919799.2018edition/utilities/... nhưng của cuốn cẩm nang nhiên Bash cũng có giới thiệu về các nhà khai thác chuyển hướng, trong đó có những cái phi tiêu chuẩn nó hỗ trợ: gnu.org/ phần mềm / bash / hướng dẫn / html_node / Redirections.html
ilkkachu

2
@ilkkachu Tôi thấy điều này được quan tâm, vì nó giải thích chi tiết về O_APPEND mà tôi đã tự hỏi về sau nhận xét của bạn :): stackoverflow.com/questions/1154446/...
jjmontes

1
@Mokubai, Bất kỳ HĐH lành mạnh nào cũng có sẵn độ dài tệp khi mở và kiểm tra cờ và di chuyển phần bù đến cuối sẽ biến mất trong tất cả các sổ sách kế toán khác. Mặc dù vậy, cố gắng thi đua O_APPENDvới lseek()nhau trước mỗi lần write()sẽ khác nhau, sẽ có thêm chi phí cuộc gọi hệ thống. (Và tất nhiên là nó sẽ không hoạt động, vì một quá trình khác có thể write()ở giữa.)
ilkkachu
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.