Tại sao hành vi của `lệnh 1> file.txt 2> file.txt` khác với` lệnh 1> file.txt 2> & 1`?


20

Khi bạn muốn chuyển hướng cả thiết bị xuất chuẩn và thiết bị xuất chuẩn vào cùng một tệp, bạn có thể thực hiện bằng cách sử dụng command 1>file.txt 2>&1hoặc command &>file.txt. Nhưng tại sao hành vi của command 1>file.txt 2>file.txthai lệnh trên lại khác nhau?

Sau đây là một lệnh xác minh.

$ cat redirect.sh
#!/bin/bash

{ echo -e "output\noutput" && echo -e "error" 1>&2; } 1>file.txt 2>&1
{ echo -e "output\noutput" && echo -e "error" 1>&2; } 1>file1.txt 2>file1.txt
{ echo -e "error" 1>&2 && echo -e "output\noutput"; } 1>file2.txt 2>file2.txt
{ echo -e "output" && echo -e "error\nerror" 1>&2; } 1>file3.txt 2>file3.txt
{ echo -e "error\nerror" 1>&2 && echo -e "output"; } 1>file4.txt 2>file4.txt

$ ./redirect.sh

$ echo "---file.txt---"; cat file.txt;\
echo "---file1.txt---"; cat file1.txt; \
echo "---file2.txt---"; cat file2.txt; \
echo "---file3.txt---"; cat file3.txt; \
echo "---file4.txt----"; cat file4.txt;
 ---file.txt---
output
output
error
---file1.txt---
error

output
---file2.txt---
output
output
---file3.txt---
error
error
---file4.txt----
output
rror

Theo như kết quả được nhìn thấy, có vẻ như chuỗi echo thứ hai sẽ ghi đè chuỗi echo đầu tiên khi bạn chạy command 1>file.txt 2>file.txt, nhưng tôi không biết tại sao nó lại như vậy. (Có một tài liệu tham khảo ở đâu đó không?)

Câu trả lời:


43

Bạn cần biết hai điều:

  • Một bộ mô tả tệp mở được biết đến ở phía chế độ ứng dụng của một quy trình tham chiếu đến một đối tượng kernel bên trong được gọi là mô tả tệp , là một thể hiện của một tệp đang mở. Có thể có nhiều mô tả tệp cho mỗi tệp và nhiều mô tả tệp chia sẻ mô tả tệp.
  • Vị trí tệp hiện tại là một thuộc tính của mô tả tệp . Vì vậy, nếu nhiều mô tả tệp ánh xạ tới một mô tả tệp duy nhất, tất cả chúng đều chia sẻ cùng một vị trí tệp hiện tại và thay đổi vị trí tệp được ban hành bằng cách sử dụng một mô tả tệp như vậy ảnh hưởng đến tất cả các mô tả tệp khác.

    Thay đổi như vậy được ban hành bởi các quá trình gọi read()/ readv(), write()/ writev(), lseek()cuộc gọi, và hệ thống như vậy. Các echolệnh cuộc gọi write()/ writev()tất nhiên.

Vì vậy, những gì xảy ra là đây:

  • command 1>file.txt 2>&1chỉ tạo một mô tả tệp, vì shell chỉ mở tệp một lần. Shell làm cho cả bản mô tả tệp lỗi tiêu chuẩn và đầu ra tiêu chuẩn ánh xạ tới mô tả tệp đơn đó. Nó sao chép đầu ra tiêu chuẩn vào lỗi tiêu chuẩn. Vì vậy, một lần ghi thông qua một trong hai bộ mô tả tệp sẽ di chuyển vị trí tệp hiện tại được chia sẻ: mỗi lần ghi đi sau lần ghi trước mô tả tệp chung. Và như bạn có thể thấy kết quả của các echolệnh không ghi đè lên nhau.
  • command 1>file.txt 2>file.txttạo hai mô tả tệp, vì shell mở cùng một tệp hai lần, để đáp ứng với hai chuyển hướng rõ ràng. Đầu ra tiêu chuẩn và mô tả tệp lỗi tiêu chuẩn ánh xạ tới hai mô tả tệp khác nhau, sau đó lần lượt ánh xạ tới cùng một tệp. Hai mô tả tệp có vị trí tệp hiện tại hoàn toàn độc lập và mỗi lần ghi ngay lập tức ghi trước đó trên cùng một mô tả tệp. Và như bạn có thể thấy kết quả là những gì được viết qua một cái có thể ghi đè lên những gì được viết qua cái khác, theo nhiều cách khác nhau tùy theo thứ tự bạn thực hiện ghi vào đó.

đọc thêm


1
Nên mở mô tả tập tin hơn là mô tả tập tin . Đó là nhiều hơn về bản ghi về cách tệp được mở nhiều hơn bản thân tệp. Đó là thuật ngữ được sử dụng bởi tài liệu POSIX, Linux, Solaris và GNU.
Stéphane Chazelas

16

Sử dụng >nói với nó để ghi đè lên tập tin. Vì bạn có stdout và stderr ghi vào tệp trong hai thao tác khác nhau, nên thao tác cuối cùng để ghi sẽ ghi đè lên đầu tiên.

Bạn có thể làm:

command 1>>file.txt 2>>file.txt

hoặc là

command &>file.txt Chỉ bash v4 trở lên.

>> bảo nó nối thêm tệp để nó không thay thế đầu ra của các hoạt động trước đó.

&> chỉ là một cách dễ dàng hơn để viết 2>&1


2
Tại sao ls 1>&0ls 0>&0vẫn hiển thị đầu ra của ls?
Yvain

Tôi ngạc nhiên khi sử dụng >>các tác phẩm. Tại sao điều này không có vấn đề về hai mô tả tập tin với độ lệch độc lập? @JdeBP, bạn có biết không? Tôi nghĩ rằng mở một tệp trong chế độ chắp thêm tương đương với mở ở chế độ ghi, tìm đến vị trí cuối cùng, sau đó không cho phép tìm kiếm thêm.
JoL

4
@jlmg: Các tệp chế độ bổ sung có thể tìm kiếm được, nhưng mỗi lần ghi được bắt đầu bằng một tìm kiếm ngụ ý đến cuối. Cho dù tìm kiếm ngụ ý là nguyên tử là ít rõ ràng đối với tôi.
Kevin

1
Điều đó hoàn toàn phụ thuộc vào câu hỏi tiếp theo. Những người như thế này có liên quan chỉ ra rằng câu trả lời là không đầy đủ. Và không chắc là mọi người sẽ tra cứu các câu hỏi tiếp theo mà không muốn biết câu trả lời đầy đủ. Do đó, nhiều khả năng những câu hỏi tiếp theo sẽ có câu trả lời trùng lặp thông tin và do đó bị đóng dưới dạng trùng lặp.
trlkly

1
@Kevin, trên các hệ thống tệp tuân thủ POSIX hoàn toàn, tìm kiếm O_APPEND ẩn là nguyên tử. Điều đó nói rằng, không phải mọi hệ thống tập tin thực hiện đúng ngữ nghĩa có liên quan - ví dụ, NFS (ít nhất là v3 và trước đó - tôi không rõ trên v4) không có hỗ trợ liên quan được đưa vào giao thức dây, vì vậy máy chủ có không có cách nào để biết nếu khách hàng mở một tập tin với O_APPEND.
Charles Duffy
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.