3>&4-
là một tiện ích mở rộng ksh93 cũng được hỗ trợ bởi bash và đó là viết tắt của 3>&4 4>&-
3, hiện tại 3 điểm chỉ đến nơi 4 được sử dụng và 4 hiện đang đóng, vì vậy, cái được chỉ ra bởi 4 giờ đã chuyển sang 3.
Việc sử dụng thông thường sẽ là trong trường hợp bạn đã sao chép stdin
hoặc stdout
lưu một bản sao của nó và muốn khôi phục nó, như trong:
Giả sử bạn muốn chụp stderr của một lệnh (và chỉ stderr) trong khi để stdout một mình trong một biến.
Thay thế lệnh var=$(cmd)
, tạo đường ống. Đầu viết của ống trở thành cmd
thiết bị xuất chuẩn (mô tả tệp 1) và đầu kia được đọc bởi trình bao để điền vào biến.
Bây giờ, nếu bạn muốn stderr
đi đến biến, bạn có thể làm : var=$(cmd 2>&1)
. Bây giờ cả fd 1 (stdout) và 2 (stderr) đều đi đến đường ống (và cuối cùng là biến), đây chỉ là một nửa so với những gì chúng ta muốn.
Nếu chúng ta làm var=$(cmd 2>&1-)
(viết tắt var=$(cmd 2>&1 >&-
), bây giờ chỉ có cmd
stderr đi vào đường ống, nhưng fd 1 đã bị đóng. Nếu cmd
cố gắng ghi bất kỳ đầu ra nào, nó sẽ trả về EBADF
lỗi, nếu nó mở một tệp, nó sẽ nhận được fd miễn phí đầu tiên và tệp mở sẽ được gán cho nó stdout
trừ khi các lệnh bảo vệ chống lại điều đó! Không phải những gì chúng ta muốn.
Nếu chúng ta muốn thiết bị xuất chuẩn bị cmd
bỏ lại một mình, nghĩa là chỉ đến cùng một tài nguyên mà nó đã chỉ ra bên ngoài thay thế lệnh, thì chúng ta cần bằng cách nào đó để đưa tài nguyên đó vào trong thay thế lệnh. Cho rằng chúng ta có thể thực hiện một bản sao stdout
bên ngoài thay thế lệnh để đưa nó vào bên trong.
{
var=$(cmd)
} 3>&1
Đó là một cách sạch hơn để viết:
exec 3>&1
var=$(cmd)
exec 3>&-
(cũng có lợi ích của việc khôi phục fd 3 thay vì đóng nó cuối cùng).
Sau đó, trên {
(hoặc exec 3>&1
) và lên đến }
, cả fd 1 và 3 đều trỏ đến cùng một tài nguyên fd 1 được trỏ đến ban đầu. fd 3 cũng sẽ trỏ đến tài nguyên đó bên trong thay thế lệnh (thay thế lệnh chỉ chuyển hướng fd 1, stdout). Vì vậy, ở trên, cmd
chúng tôi đã có các fds 1, 2, 3:
- đường ống đến var
- hoang sơ
- giống như những gì 1 điểm bên ngoài thay thế lệnh
Nếu chúng ta thay đổi nó thành:
{
var=$(cmd 2>&1 >&3)
} 3>&1-
Sau đó, nó trở thành:
- giống như những gì 1 điểm bên ngoài thay thế lệnh
- đường ống đến var
- giống như những gì 1 điểm bên ngoài thay thế lệnh
Bây giờ, chúng ta đã có những gì chúng ta muốn: stderr đi đến đường ống và thiết bị xuất chuẩn không bị ảnh hưởng. Tuy nhiên, chúng tôi đang rò rỉ rằng fd 3 đến cmd
.
Trong khi các lệnh (theo quy ước) giả sử fds 0 đến 2 là mở và là đầu vào, đầu ra và lỗi tiêu chuẩn, chúng không giả sử bất cứ thứ gì của các fds khác. Nhiều khả năng họ sẽ để lại fd 3 mà không bị ảnh hưởng. Nếu họ cần một bộ mô tả tệp khác, họ sẽ chỉ làm một open()/dup()/socket()...
bộ mô tả tệp có sẵn đầu tiên. Nếu (giống như một tập lệnh shell nào exec 3>&1
) mà họ cần sử dụng fd
cụ thể, trước tiên họ sẽ gán nó cho một cái gì đó (và trong quá trình đó, tài nguyên do fd 3 của chúng tôi nắm giữ sẽ được phát hành theo quy trình đó).
Đó là một thực hành tốt để đóng fd 3 vì cmd
không sử dụng nó, nhưng sẽ không có vấn đề gì lớn nếu chúng tôi để nó được chỉ định trước khi chúng tôi gọi cmd
. Các vấn đề có thể là: rằng cmd
(và có khả năng các quá trình khác mà nó sinh ra) có ít hơn một fd có sẵn cho nó. Một vấn đề nghiêm trọng hơn tiềm năng là nếu tài nguyên mà fd trỏ đến có thể bị giữ bởi một quá trình được sinh ra bởi cmd
nền đó. Nó có thể là một mối quan tâm nếu tài nguyên đó là một đường ống hoặc kênh liên lạc giữa các quá trình khác (như khi tập lệnh của bạn đang được chạy script_output=$(your-script)
), vì điều đó có nghĩa là quá trình đọc từ đầu kia sẽ không bao giờ thấy kết thúc của tập tin cho đến khi quá trình nền chấm dứt.
Vì vậy, ở đây, tốt hơn là viết:
{
var=$(cmd 2>&1 >&3 3>&-)
} 3>&1
Mà, với bash
có thể rút ngắn thành:
{
var=$(cmd 2>&1 >&3-)
} 3>&1
Để tóm tắt lý do tại sao nó hiếm khi được sử dụng:
- đó là đường không chuẩn và chỉ là cú pháp. Bạn đã phải cân bằng việc lưu một vài tổ hợp phím bằng cách làm cho tập lệnh của bạn ít di động hơn và ít rõ ràng hơn đối với những người không quen với tính năng không phổ biến đó.
- Nhu cầu đóng fd ban đầu sau khi sao chép nó thường bị bỏ qua vì hầu hết thời gian, chúng tôi không phải chịu hậu quả, vì vậy chúng tôi chỉ làm
>&3
thay vì >&3-
hoặc >&3 3>&-
.
Bằng chứng là nó hiếm khi được sử dụng, như bạn phát hiện ra rằng nó không có thật trong bash . Trong bash compound-command 3>&4-
hoặc any-builtin 3>&4-
lá fd 4 đóng ngay cả sau khi compound-command
hoặc any-builtin
đã trở lại. Một bản vá để khắc phục sự cố hiện đã có (2013/02/19).