Tôi muốn hỏi:
Tại sao được echo {1,2,3}
mở rộng thành 1 2 3 là hành vi được mong đợi, trong khi echo [[:digit:]]
trả về [[:digit:]]
trong khi tôi dự kiến nó sẽ in tất cả các chữ số từ 0
đến 9
?
Tôi muốn hỏi:
Tại sao được echo {1,2,3}
mở rộng thành 1 2 3 là hành vi được mong đợi, trong khi echo [[:digit:]]
trả về [[:digit:]]
trong khi tôi dự kiến nó sẽ in tất cả các chữ số từ 0
đến 9
?
Câu trả lời:
Bởi vì chúng là hai thứ khác nhau. Đây {1,2,3}
là một ví dụ về mở rộng cú đúp . Cấu {1,2,3}
trúc được mở rộng bằng vỏ , echo
thậm chí trước khi nhìn thấy nó. Bạn có thể thấy những gì xảy ra nếu bạn sử dụng set -x
:
$ set -x
$ echo {1,2,3}
+ echo 1 2 3
1 2 3
Như bạn có thể thấy, lệnh echo {1,2,3}
được mở rộng thành:
echo 1 2 3
Tuy nhiên, [[:digit:]]
là một lớp nhân vật POSIX . Khi bạn đưa nó vào echo
, shell cũng sẽ xử lý nó trước, nhưng lần này nó đang được xử lý như một shell toàn cầu . nó hoạt động tương tự như khi bạn chạy echo *
sẽ in tất cả các tệp trong thư mục hiện tại. Nhưng [[:digit:]]
là một vỏ toàn cầu sẽ phù hợp với bất kỳ chữ số. Bây giờ, trong bash, nếu một vỏ toàn cầu không khớp với bất cứ thứ gì, nó sẽ được mở rộng thành chính nó:
$ echo /this*matches*no*files
+ echo '/this*matches*no*files'
/this*matches*no*files
Nếu quả địa cầu khớp với thứ gì đó, nó sẽ được in:
$ echo /e*c
+ echo /etc
/etc
Trong cả hai trường hợp, echo
chỉ cần in bất cứ thứ gì mà vỏ bảo nó in, nhưng trong trường hợp thứ hai, vì quả cầu khớp với thứ gì đó ( /etc
) nó được yêu cầu in thứ gì đó.
Vì vậy, vì bạn không có bất kỳ tệp hoặc thư mục nào có tên bao gồm chính xác một chữ số (đó là những gì [[:digit:]]
sẽ khớp), toàn cầu được mở rộng thành chính nó và bạn nhận được:
$ echo [[:digit:]]
[[:digit:]]
Bây giờ, hãy thử tạo một tệp được gọi 5
và chạy cùng một lệnh:
$ echo [[:digit:]]
5
Và nếu có nhiều hơn một tệp phù hợp:
$ touch 1 5
$ echo [[:digit:]]
1 5
Đây là (sắp xếp) tài liệu man bash
trong phần giải thích các nullglob
tùy chọn tắt hành vi này:
nullglob
If set, bash allows patterns which match no files (see
Pathname Expansion above) to expand to a null string,
rather than themselves.
Nếu bạn đặt tùy chọn này:
$ rm 1 5
$ shopt -s nullglob
$ echo [[:digit:]] ## prints nothing
$
shopt -s failglob
để có được một hành vi hữu ích hơn tương tự như các shell hiện đại như zsh
hay fish
.
failglob
. nullglob
có thể gây ra sự cố không mong muốn, ví dụ: khi dán URL xảy ra có ?
.
nullglob
để chứng minh rằng mô hình đang được giải thích như một quả địa cầu bằng vỏ.
{1,2,3}
là mở rộng cú đúp , nó mở rộng đến các từ được liệt kê mà không liên quan đến ý nghĩa của chúng.
[...]
là một nhóm ký tự, được sử dụng trong mở rộng tên tệp (hoặc ký tự đại diện hoặc toàn cầu) tương tự như dấu hoa thị *
và dấu hỏi ?
. Nó phù hợp với bất kỳ ký tự đơn nào được liệt kê bên trong hoặc các ký tự là thành viên của các nhóm được đặt tên, chẳng hạn như [:digit:]
nếu chúng được liệt kê. Hành vi mặc định của hầu hết các shell là để nguyên ký tự đại diện nếu không có tệp nào khớp với nó.
.
Vì thế:
$ bash -c 'echo [[:digit:]]' # bash leaves it as-is
[[:digit:]]
$ zsh -c 'echo [[:digit:]]' # zsh by default complains if no match
zsh:1: no matches found: [[:digit:]]
$ touch 1 3 d i g t
$ bash -c 'echo [[:digit:]]' # now there are two matches
1 3 # note that d, i, g and t do NOT match
Nhưng vẫn:
$ bash -c 'echo {1,2,3}'
1 2 3
Cả hai đều được mở rộng bằng shell , không thành vấn đề nếu lệnh bạn đang chạy là ls
, echo
hoặc rm
. Cũng lưu ý rằng nếu một trong hai được trích dẫn, chúng sẽ không được mở rộng:
$ bash -c 'echo "[[:digit:]]"' # even though matching files still exist
[[:digit:]]
$ bash -c 'echo "{1,2,3}"'
{1,2,3}
[[:digit:]]
trước khi chuyển nó sang echo
, vì vậy echo
không bao giờ nhìn thấy [[:digit:]]
, nó chỉ nhìn thấy 1 3
. Bạn có thể thấy điều này bằng hành động bằng cách chạy set -x
sẽ in các lệnh thực tế đang chạy (chạy set +x
để tắt lại).
echo
không tìm kiếm tệp, trình bao , trước khi chạy echo
.
{1,2,3}
(ví dụ và {1..3}
đang mở rộng cú đúp . Họ được giải thích bởi vỏ trước khi thực hiện lệnh.
[[:digit:]]
là mã thông báo khớp mẫu , nhưng bạn không sử dụng nó ở một vị trí có bất kỳ tệp nào khớp với mẫu đó. Nếu bạn sử dụng khớp mẫu không khớp, nó sẽ tự mở rộng:
$ echo [[:digit:]]; touch 3; echo [[:digit:]]
[[:digit:]]
3