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


61

Trong hầu hết các shell nullglobkhông phải là mặc định. Điều đó có nghĩa là, ví dụ, nếu bạn chạy lệnh này

ls *

trong một thư mục trống, nó sẽ mở rộng toàn *cầu thành một chữ *, thay vào đó là một danh sách các đối số trống. Có nhiều cách để thay đổi hành vi đó, để *trong một thư mục trống sẽ trả về một danh sách trống các đối số, có vẻ trực quan hơn.

Vì vậy, có một lý do tại sao nullglobbị tắt theo mặc định? Nếu vậy, lý do đó là gì?


13
Ai đó đã từng đưa ra một lựa chọn tồi tệ biến thành "chúng tôi luôn làm theo cách này". Một hiện tượng rất phổ biến (không chỉ) trong thế giới phần mềm.
PSkocik

4
Lý do ban đầu là tùy chọn nullglob không tồn tại trước đó. Vì vậy, để duy trì khả năng tương thích ngược, nó phải được tắt theo mặc định.
PM 2Ring

3
Mặc dù không đề cập cụ thể đến toàn cầu null, nhưng một số lịch sử trên toàn cầu được cung cấp bởi câu trả lời này: unix.stackexchange.com/a/136409/22724
StrongBad

4
Tôi có ấn tượng một số người nghĩ rằng rõ ràng là nullglob nên được bật theo mặc định. Tôi không nghĩ nó rõ ràng. Sự mở rộng đó xảy ra trong trình bao trước khi gọi lệnh có nghĩa là mở rộng thành không có gì là một hành vi ít trực quan hơn so với toàn cầu không thay đổi.
kojiro

2
@kojiro Ít trực quan với ai? Bất cứ ai có bất kỳ sự quen thuộc nào với các vỏ * NIX đều biết rằng đó *là một quả địa cầu và mở rộng ra tất cả các tệp hiện có ; Làm thế nào là "trực quan" để có một trường hợp đặc biệt trong đó các khối thư mục trống được "mở rộng" thành một nghĩa đen *?
Kyle Strand

Câu trả lời:


78

Các nullglobtùy chọn (mà BTW là một zshsáng chế, chỉ thêm vào năm sau để bash( 2.0)) sẽ không được lý tưởng trong một số trường hợp. Và lslà một ví dụ tốt:

ls *.txt

Hoặc tương đương chính xác hơn của nó:

ls -- *.txt

Với nullglobon sẽ chạy lsmà không có đối số nào được coi là ls -- .(liệt kê thư mục hiện tại) nếu không có tệp nào khớp, điều này có thể tệ hơn so với việc gọi lsvới một *.txtđối số là nghĩa đen .

Bạn sẽ gặp vấn đề tương tự với hầu hết các tiện ích văn bản:

grep foo *.txt

Sẽ tìm kiếm footrên stdin nếu không có txttập tin.

Một mặc định hợp lý hơn và một trong các csh, tcsh, zsh hoặc fish 2.3+ (và các shell Unix đầu tiên) là hủy bỏ lệnh hoàn toàn nếu toàn cầu không khớp.

bash(kể từ phiên bản 3) có một failglobtùy chọn cho điều đó (thú vị với cuộc thảo luận này, vì trái với ash, AT & T kshhoặc zsh, bashkhông hỗ trợ phạm vi địa phương cho các tùy chọn (mặc dù điều đó thay đổi trong 4.4), tùy chọn đó khi được bật trên toàn cầu sẽ phá vỡ một số điều như các hàm hoàn thành bash).

Lưu ý rằng csh và tcsh là hơi khác nhau từ zsh, fishhoặc bash -O failglobtrong các trường hợp như:

ls -- *.txt *.html

Nơi bạn cần tất cả các khối để không khớp để lệnh bị hủy. Chẳng hạn, nếu có một tệp txt và không có tệp html, thì đó là:

ls -- file.txt

Bạn có thể có hành vi đó zshvới setopt cshnullglobmặc dù một cách hợp lý hơn để thực hiện nó zshsẽ là sử dụng một quả địa cầu như:

ls -- *.(txt|html)

Trong zshksh93, bạn cũng có thể áp dụng nullglob trên cơ sở toàn cầu, đây là cách tiếp cận hiệu quả hơn nhiều so với sửa đổi cài đặt toàn cầu:

files=(*.txt(N))  # zsh
files=(~(N)*.txt) # ksh93

sẽ tạo ra một mảng trống nếu không có txttệp thay vì thất bại lệnh có lỗi (hoặc biến nó thành một mảng với một *.txtđối số bằng chữ với các shell khác).

Các phiên bản fishtrước 2.3 sẽ hoạt động như thế bash -O nullglobnhưng đưa ra cảnh báo khi tương tác khi toàn cầu không khớp. Kể từ 2.3, nó hoạt động giống như zshngoại trừ các khối được sử dụng trong for, sethoặc count.

Bây giờ, trên ghi chú lịch sử, hành vi đã thực sự bị phá vỡ bởi vỏ Bourne. Trong các phiên bản trước của Unix, việc tạo hình được thực hiện thông qua trình /etc/globtrợ giúp và người trợ giúp đó đã hành xử như cshsau: nó sẽ không thực hiện được lệnh nếu không có bất kỳ khối nào khớp với bất kỳ tệp nào và loại bỏ các khối không có kết quả trùng khớp.

Vì vậy, tình huống chúng ta gặp phải hôm nay là do một quyết định tồi tệ được đưa ra trong vỏ Bourne.

Lưu ý rằng shell Bourne (và shell C) đi kèm với một tính năng Unix mới khác: môi trường. Điều đó có nghĩa là mở rộng biến (tiền thân của nó chỉ có $1, $2... tham số vị trí). Shell Bourne cũng giới thiệu thay thế lệnh.

Một quyết định thiết kế kém khác của shell Bourne là thực hiện toàn cầu hóa (và tách) khi mở rộng các biến và thay thế lệnh (có thể để tương thích ngược với shell Thompson, nơi echo $1vẫn sẽ gọi /etc/globnếu $1chứa các ký tự đại diện (nó giống như mở rộng macro tiền xử lý ở đó, như trong giá trị mở rộng đã được phân tích cú pháp lại dưới dạng mã shell)).

Không có những quả cầu không phù hợp có nghĩa là:

pattern='a.*b'
grep $pattern file

sẽ thất bại lệnh (trừ khi có một số a.whateverbtệp trong thư mục hiện tại). csh(cũng thực hiện toàn cầu khi mở rộng biến) không thực hiện được lệnh trong trường hợp đó (và tôi cho rằng tốt hơn là để lại một lỗi không hoạt động ở đó, ngay cả khi nó không tốt như không thực hiện toàn cầu như trong zsh).


Có một vấn đề về khả năng sử dụng khác: nullglobdường như phá vỡ hoàn thành tab (nhấn phím tab không làm gì khi được bật).
Kyle Strand

1
Có thể sử dụng nullglob trên cơ sở toàn cầu với bash - mặc dù cú pháp không thanh lịch như zshfiles=$(shopt -s nullglob;echo *.txt)
Jon Nalley

2
@JonNalley, lưu trữ phần nối (có khoảng trắng) của tên tệp (có thể chuyển đổi với xpg_echo) thành một biến vô hướng . Bạn cần một cái gì đó như readarray -td '' files < <(shopt -s nullglob; printf '%s\0' *.txt)với bash4.4 trở lên hoặc (shopt -s nullglob; printf '%s\0' *.txt) | xargs -r0 cmdvới GNU xargsđể có thể sử dụng được tất cả các tên tệp tùy ý. Hoặc, vẫn với bash4.4, sử dụng hàm trợ giúp sử dụng local -(được sao chép từ tro 25 năm sau) cho phạm vi cục bộ cho các tùy chọn.
Stéphane Chazelas
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.