Câu trả lời:
Một lý do đơn giản là sự tồn tại của một ký tự : space.
Mở rộng cú đúp không xử lý không gian (không trích dẫn).
Một {...}
danh sách cần không gian (không trích dẫn).
Câu trả lời chi tiết hơn là cách shell phân tích một dòng lệnh .
Bước đầu tiên để phân tích (hiểu) một dòng lệnh là chia nó thành nhiều phần.
Các phần này (thường được gọi là từ hoặc mã thông báo) là kết quả của việc phân chia một dòng lệnh tại mỗi ký tự meta từ liên kết :
- Tách lệnh thành các mã thông báo được phân tách bằng tập hợp các ký tự meta cố định: SPACE, TAB, NEWLINE,;, (,), <,>, |, và &. Các loại mã thông báo bao gồm từ, từ khóa, chuyển hướng I / O và dấu chấm phẩy.
Siêu ký tự: spacetabenter;,<>|và &.
Sau khi tách, các từ có thể thuộc loại (theo cách hiểu của trình bao):
LC=ALL ...
LC=ALL echo
LC=ALL echo "hello"
LC=ALL echo "hello" >&2
Chỉ khi "chuỗi cú đúp" (không có dấu cách hoặc ký tự meta) là một từ đơn (như được mô tả ở trên) và không được trích dẫn , thì đó là một ứng cử viên cho "Mở rộng cú đúp ". Nhiều kiểm tra được thực hiện trên cấu trúc nội bộ sau này.
Như vậy, điều này: {ls,-l}
đủ tiêu chuẩn là "Brace mở rộng" để trở thành ls -l
, hoặc là first word
hoặc argument
(trong bash, zsh là khác nhau).
$ {ls,-l} ### executes `ls -l`
$ echo {ls,-l} ### prints `ls -l`
Nhưng điều này sẽ không : {ls ,-l}
. Bash sẽ phân tách spacevà phân tích dòng thành hai từ: {ls
và ,-l}
sẽ kích hoạt một command not found
(đối số ,-l}
bị mất):
$ {ls ,-l}
bash: {ls: command not found
Dòng của bạn: {ls;echo hi}
sẽ không trở thành "Mở rộng cú đúp" vì hai ký tự meta ;và space.
Nó sẽ được chia thành ba phần này: {ls
lệnh mới : echo
hi}
. Hiểu rằng ;kích hoạt bắt đầu một lệnh mới. Lệnh {ls
sẽ không được tìm thấy và lệnh tiếp theo sẽ in hi}
:
$ {ls;echo hi}
bash: {ls: command not found
hi}
Nếu nó được đặt sau một số lệnh khác, thì dù sao nó cũng sẽ bắt đầu một lệnh mới sau ;:
$ echo {ls;echo hi}
{ls
hi}
Một trong những "lệnh ghép" là "Danh sách cú đúp" (từ của tôi) : { list; }
.
Như bạn có thể thấy, nó được xác định bằng dấu cách và đóng ;
.
Các khoảng trắng và ;là cần thiết bởi vì cả hai {
và }
là " Từ dành riêng ".
Và do đó, để được công nhận là từ, phải được bao quanh bởi các ký tự meta (hầu như luôn luôn space:).
Như được mô tả trong điểm 2 của trang được liên kết
- Kiểm tra mã thông báo đầu tiên của mỗi lệnh để xem nó có phải là ...., {hoặc (, sau đó lệnh thực sự là một lệnh ghép.
Ví dụ của bạn: {ls;echo hi}
không phải là một danh sách.
Nó cần một đóng cửa ;và một không gian (ít nhất) sau {. Cuối cùng }được xác định bởi việc đóng cửa ;.
Đây là một danh sách { ls;echo hi; }
. Và đây { ls;echo hi;}
cũng là (ít được sử dụng, nhưng hợp lệ) (Cảm ơn @choroba đã giúp đỡ).
$ { ls;echo hi; }
A-list-of-files
hi
Nhưng là đối số (shell biết sự khác biệt) với một lệnh, nó gây ra lỗi:
$ echo { ls;echo hi; }
bash: syntax error near unexpected token `}'
Nhưng hãy cẩn thận với những gì bạn tin rằng shell đang phân tích cú pháp:
$ echo { ls;echo hi;
{ ls
hi
;
và }
. { ls;}
hoạt động như dấu chấm phẩy đã là một siêu ký tự.
Khối {
là một từ khóa shell, vì vậy nó phải tách biệt với từ tiếp theo theo khoảng trắng, trong khi mở rộng dấu ngoặc, không nên có khoảng trắng (nếu bạn cần niềng mở rộng một khoảng trắng, bạn phải thoát nó echo {\ ,a}{b,c}
:).
Bạn có thể sử dụng mở rộng dấu ngoặc khi bắt đầu lệnh:
{ls,.} # expands to "ls ."
Tuy nhiên, bạn không thể sử dụng nó để mở rộng thành một khối, vì việc phân tích các lệnh nhóm xảy ra trước khi mở rộng:
echo {'{ ls','.;}'} # { ls .;}
{'{ ls','.;}'} # bash: { ls: No such file or directory
Nó biết bằng cách kiểm tra cú pháp của dòng lệnh. Theo cùng một cách nó biết rằng trong biểu thức echo echo
, tiếng vang đầu tiên phải được coi là một lệnh và tiếng vang thứ hai là một tham số của tiếng vang đầu tiên.
Trong bash nó rất đơn giản, vì { cmd; }
nên có dấu cách và dấu chấm phẩy. Tuy nhiên, ví dụ trong zsh, chúng không cần thiết, nhưng vẫn bằng cách phân tích bối cảnh của {}
shell có thể cho biết những gì nên được thực hiện với nội dung của nó.
Hãy xem xét những điều sau đây:
alias 1..3=date
{ 1..3; } #in bash
{1..3} #in zsh
Cả hai trả về ngày hiện tại, nhưng
echo {1..3}
trả về 1 2 3
vì shell biết {}
trong một đối số cho lệnh echo
, vì vậy nên được mở rộng.
{
theo sau là không gian không trích dẫn không bắt đầu mở rộng cú đúp trong bash.
{
. Không gian không trích dẫn không thể ở bất cứ đâu vì shell chia toàn bộ dòng lệnh trên khoảng trắng.
Đầu tiên, một dấu ngoặc kép phải là một từ của chính nó và từ đầu tiên của dòng lệnh:
echo { these braces are just words }
Thứ hai, niềng răng riêng lẻ không đặc biệt (như bạn có thể thấy ở trên). Niềng răng trống cũng không đặc biệt:
echo {} # just the token {}: familiar from the find command
Bất cứ điều gì mà không có dấu phẩy cũng chỉ là chính nó
echo {abc} # just {abc}
Đây là nơi hành động bắt đầu.
echo {a,b} # braces disappear, a b results.
Vì vậy, về cơ bản để mở rộng cú đúp để khởi động, chúng ta cần một từ duy nhất (không được tách thành các trường trên khoảng trắng), bên trong đó xảy ra ít nhất một trường hợp {...}
bên trong có ít nhất một dấu phẩy.
Nhân tiện, đây có thể là từ đầu tiên trong dòng lệnh:
{ls,-l} . # just "ls -l ."
{
hiểu là một danh sách lệnh nếu nó xuất hiện ở đầu lệnh và dưới dạng mở rộng dấu ngoặc, nhưng tôi không chắc chắn.