Mở rộng với * .txt trong trình bao không hoạt động nếu không có tệp .txt


10

Tôi đang chơi xung quanh với sự mở rộng, và tôi nhận thấy một hành vi kỳ dị. Tôi đã thử làm:

echo ./*.txt

Và tôi không có tập tin .txt nào trong thư mục hiện tại của mình. Đầu ra tôi nhận được là:

./*.txt

Tôi chỉ tò mò: Tại sao tôi lại nhận được thứ này? Tôi đã mong đợi để không nhận được bất kỳ đầu ra.

PS: Khi tôi đã có một .txttập tin, việc mở rộng đã được giải thích chính xác. Nói cách khác, giả sử tôi có một tập tin, smthn.txttiếng vang thực sự vang vọng current_directory/smthn.txt.

Câu trả lời:


15

Thích ứng từ trang bash shell man,

bash quét từng từ cho các ký tự *,?, và [. Nếu một trong những ký tự này xuất hiện, thì từ đó được coi là một mẫu và được thay thế bằng một danh sách tên tệp được sắp xếp theo thứ tự bảng chữ cái phù hợp với mẫu. Nếu không tìm thấy tên tệp phù hợp và tùy chọn shell nullglob không được bật, từ này sẽ không thay đổi. Nếu tùy chọn nullglob được đặt và không tìm thấy kết quả trùng khớp, từ đó sẽ bị xóa.

Trong trường hợp này, tôi cho rằng nullglob không được kích hoạt, vì vậy từ này được giữ nguyên - do đó đầu ra bạn nhìn thấy.


2
Bạn có thể thay đổi hành vi như sau: shopt -s nullglobsẽ tạo ra các chuỗi trống cho các mẫu không khớp và shopt -u nullglob(cài đặt tiêu chuẩn) sẽ tự tạo ra mẫu.
PerlDuck

13

Tôi đã mong đợi để không nhận được bất kỳ đầu ra.

Nếu nullgloblà mặc định, nhiều lệnh sẽ hoạt động khá bất ngờ, vì thông thường (có lẽ không may) các lệnh xử lý trường hợp đối số tên tệp bằng 0 theo cách khác về mặt chất lượng so với trường hợp của một hoặc nhiều đối số tên tệp.

Giả sử bạn đã bật nullglob( shopt -s nullglob) và bạn đang ở trong một thư mục không có tệp nào khớp *.txt. Sau đó, *.txtthực sự sẽ mở rộng thành không có gì - không phải là một trường trống, nhưng không có trường nào cả - như bạn mong đợi. Nhưng điều đó sẽ có những kết quả này:

  • ls *.txtsẽ liệt kê tất cả các tệp trong thư mục hiện tại (ngoại trừ các tệp bị ẩn), vì đó là những gì lskhi bạn không vượt qua bất kỳ đối số tên tệp nào.
  • cat *.txtsẽ đọc từ đầu vào tiêu chuẩn , bởi vì khi catkhông có đối số tên tệp, nó như thể bạn đã chạy cat -. Nếu chạy tương tác, nó ngồi xung quanh chờ đầu vào. Nhiều lệnh hành xử theo cách này.
  • cp *.txt dest/sẽ thất bại với lỗi cp: missing destination file operand after 'dest/'. Đây không phải là một thảm họa, nhưng nó khó hiểu và hoàn toàn khác với thành công thầm lặng có lẽ là mong muốn.
  • file *.txtvà nhiều chương trình khác không có hành vi đặc biệt đối với trường hợp không có đối số tên tệp, vẫn không thành công với lỗi hoặc thông báo sử dụng khi không có thông báo nào được thông qua.
  • Ngay cả những trường hợp trực giác cảm thấy như họ nên làm việc thường sẽ không. printf 'Got file: "%s"\n' *.txtsẽ in Got file: ""thay vì không có gì.
  • Thất bại tình cờ để báo xuất hiện của *, ?[rằng không có ý định để được mở rộng bằng vỏ thường xuyên hơn sẽ tạo ra kết quả rõ ràng là sai, nhưng theo những cách mà có thể khó khăn để tìm ra. Ví dụ: nếu không có tên tệp trong thư mục hiện tại bắt đầu gedit, thì apt list gedit*(nơi apt list 'gedit*'được dự định) sẽ trở thành chính xác apt listvà liệt kê tất cả các gói có sẵn.

Vì vậy, thật tốt khi bạn không có hành vi này mà không yêu cầu nó. Có lẽ tình huống thực tế phổ biến nhất thực sự được đơn giản hóa nullglobfor f in *.txt. Xem thêm câu hỏi này (mà câu trả lời của Sergiy Kolodyazhnyy liên quan đến).

Câu hỏi khó hơn để trả lời là tại sao failglob- ở đâu đó là lỗi mở rộng để có một quả cầu không khớp với bất kỳ tệp nào - không phải là mặc định trong bash. Tôi tin rằng câu trả lời của Sergiy Kolodyazhnyy đã nắm bắt được lý do cho việc này ngay cả khi không giải quyết trực tiếp. Giữ lại các khối lượng chưa được mở rộng mà không tạo ra lỗi mở rộng (có lẽ không may) là hành vi được tiêu chuẩn hóa, và nó cũng là hành vi truyền thống, và do đó, được mong đợi. Mặc dù bash không cố gắng hoàn toàn tuân thủ POSIX trừ khi được gọi bằng tên shhoặc thông qua --posixtùy chọn, nhiều lựa chọn thiết kế của nó ngay cả khi không ở chế độ POSIX theo trực tiếp POSIX. Họ đã phải chọn một số hành vi và có những nhược điểm liên quan đến việc đi ngược lại mong đợi của người dùng.


Tôi nghĩ rằng đây là khía cạnh ít ảnh hưởng nhất trong lịch sử của vấn đề nên tôi đã lưu nó lần cuối ... nhưng điều đáng nói là có một điều gì đó hơi kỳ quặc về mặt nullglobhành vi.

nullglobThoạt nhìn có vẻ thanh lịch bởi vì, về mặt cú pháp , nó xử lý trường hợp các tệp không khớp không khác với trường hợp của một, hai hoặc bất kỳ số nào khác. Các lệnh chúng ta chạy, mà các khối được mở rộng thành các đối số, không có xu hướng đối xử với chúng giống như chi tiết ở trên. Nhưng về mặt cú pháp thì điều này ít nhất cảm thấy đúng, mà tôi nghĩ là động lực cho câu hỏi của bạn.

Tuy nhiên, có một sự mâu thuẫn khác, tinh tế hơn nullglobkhông giải quyết được - đó là nó thực sự khuếch đại. Trường hợp các ký tự toàn cầu bằng không ("ký tự đại diện") được xử lý hoàn toàn khác với trường hợp của một hoặc hai hoặc bất kỳ số nào khác. Ví dụ, với shopt -s nullglob, nếu ab?d?fkhông khớp với bất kỳ tệp nào, nó sẽ bị xóa; nếu ab?dkhông khớp với bất kỳ tập tin nào, nó sẽ bị xóa; nhưng nếu abkhông khớp với bất kỳ tệp nào (nghĩa là, nếu không có tệp nào có tên chính xác ab) thì nó vẫn không bị xóa. Tất nhiên, nó sẽ là một thảm họa nếu nó bị xóa, bởi vì nó có thể không có ý định tham khảo một tệp hiện có trong thư mục hiện tại; nó thậm chí có thể không đề cập đến một tập tin. Nhưng điều này vẫn loại bỏ bất kỳ hy vọng cho sự nhất quán.

Ba hành vi bash cung cấp - mặc định đối xử với các khối u không khớp với bất kỳ tệp nào như thể chúng không phải là các khối và chuyển chúng chưa được mở rộng, hành vi mà bạn mong đợi khi xử lý chúng (nếu bạn bỏ qua cụm từ kỳ quặc này) như biểu thị tất cả số không của các tệp khớp với ( nullglob) và hành vi an toàn khi xem xét chúng là lỗi ( failglob) - tất cả đều thể hiện các cách tiếp cận khác nhau đối với sự mơ hồ vốn có trong vỏ không thể biết liệu có bất kỳ từ cụ thể nào được dự định là tên tệp. Shell thực hiện các mở rộng của nó mà không có kiến ​​thức về cách các lệnh cụ thể mà bạn gọi với nó sẽ xử lý các đối số của chúng.

Đây là một trong nhiều trường hợp phân tách mối quan tâm . Trong các hệ thống có thiết kế tuân theo triết lý Unix, mỗi phần được dự định làm một việc và làm tốt . Shell xử lý văn bản thành các lệnh và đối số và gọi các lệnh đó, hầu hết trong số đó là bên ngoài chính shell. Điều này có xu hướng đẹp hơn và linh hoạt hơn nhiều so với các hệ thống mà các lệnh bên ngoài tự chịu trách nhiệm thực hiện các biến đổi đó (như với các bộ xử lý lệnh truyền thống trong DOS và Windows). Nhưng nó có nhược điểm thường xuyên của nó.


Rõ ràng, bash-4.3,39 (2) không có failglob. Vì vậy, nó không thể là mặc định vì nó không luôn được hỗ trợ.
Ruslan

6

Lý do chính là vì đây là hành vi tiêu chuẩn được chỉ định bởi POSIX - tiêu chuẩn bao gồm ngôn ngữ lệnh shell và trong số những thứ khác khớp mẫu (shell như bash, dashshell - mặc định của Ubuntu /bin/shkshtuân theo tiêu chuẩn này). Từ phần 2.13.3 Các mẫu được sử dụng để mở rộng tên tệp :

Nếu mẫu không khớp với bất kỳ tên tệp hoặc tên đường dẫn hiện có nào, chuỗi mẫu sẽ được giữ nguyên.

Điều này tất nhiên có tác dụng phụ - tên tệp phù hợp có thể theo nghĩa đen *.txt. Các nullglobtùy chọn trong bashzshcó thể giúp: nếu tùy chọn được kích hoạt qua shopt -s nullglob(và nó không được kích hoạt theo mặc định áp dụng cho câu hỏi này), sau đó globstar sẽ được mở rộng để chuỗi rỗng khi không có tên tập tin phù hợp được tìm thấy. ksh93có cơ chế khớp mẫu tiên tiến của riêng nó mà đạt được hiệu quả tương tự~(N)*.txt

Xem thêm Tại sao nullglob không được mặc định?

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.