Làm thế nào là dấu ngoặc kép trong bash khớp (ghép)?


15

Tôi đang sử dụng GNU bash 4.3.48. Hãy xem xét hai lệnh sau chỉ khác nhau bởi một ký hiệu đô la duy nhất.

Lệnh 1:

echo "(echo " * ")"

Lệnh 2:

echo "$(echo " * ")"

Đầu ra của chúng, tương ứng

(echo  test.txt ppcg.sh )

 * 

Vì vậy, rõ ràng trong trường hợp đầu tiên *là hình cầu, có nghĩa là dấu ngoặc kép đầu tiên đi với cái thứ hai để tạo thành một cặp, và thứ ba và thứ tư tạo thành một cặp khác.

Trong trường hợp thứ hai, *nó không được tạo thành hình cầu, và có chính xác hai khoảng trắng ở đầu ra, một trước dấu hoa thị và một sau, có nghĩa là dấu ngoặc kép thứ hai đi với cái thứ ba và cái đầu tiên đi với cái thứ tư.

Có trường hợp nào khác ngoài việc $()xây dựng mà dấu ngoặc kép không khớp với cái tiếp theo, nhưng được lồng vào nhau không? Hành vi này có được ghi chép tốt không và nếu có, tôi có thể tìm tài liệu tương ứng ở đâu?



Một lần nữa, cú pháp tô sáng trong UL SE là sai và gây hiểu lầm, mặc dù việc tô điểm cho JS là điều đáng trách.
Weijun Zhou

Câu trả lời:


14

Bất kỳ cấu trúc lồng nhau nào có thể được nội suy bên trong các chuỗi có thể có thêm các chuỗi bên trong chúng: chúng được phân tích cú pháp như một tập lệnh mới, cho đến điểm đánh dấu đóng và thậm chí có thể được lồng sâu nhiều cấp. Tất cả thanh một trong những bắt đầu với một $. Tất cả chúng đều được ghi lại trong tài liệu kết hợp giữa hướng dẫn sử dụng Bash và đặc tả ngôn ngữ lệnh shell POSIX.

Có một vài trường hợp của các cấu trúc này:

  • Thay thế lệnh bằng$( ... ) , như bạn đã tìm thấy. POSIX chỉ định hành vi này :

    Với $(command)biểu mẫu, tất cả các ký tự theo dấu ngoặc đơn mở cho dấu ngoặc đơn đóng phù hợp sẽ tạo thành lệnh. Bất kỳ tập lệnh shell hợp lệ nào cũng có thể được sử dụng cho lệnh ...

    Trích dẫn là một phần của tập lệnh shell hợp lệ, vì vậy chúng được cho phép với ý nghĩa thông thường của chúng.

  • Thay thế lệnh bằng cách sử dụng `, quá.
  • Phần tử "từ" của các trường hợp thay thế tham số nâng cao như${parameter:-word} . Các định nghĩa của "từ" là :

    Một chuỗi các ký tự được coi là một đơn vị của vỏ

    - bao gồm văn bản được trích dẫn và thậm chí cả trích dẫn hỗn hợp a"b"c'd'e- mặc dù hành vi thực tế của việc mở rộng là một chút tự do hơn thế, và ví dụ ${x:-hello world}cũng hoạt động.

  • Mở rộng số học với $(( ... )), mặc dù phần lớn là vô dụng ở đó (nhưng bạn cũng có thể lồng thay thế lệnh hoặc mở rộng biến, và sau đó có các trích dẫn hữu ích bên trong chúng). POSIX nói rằng :

    Biểu thức phải được xử lý như thể nó nằm trong dấu ngoặc kép, ngoại trừ trích dẫn kép bên trong biểu thức không được xử lý đặc biệt. Shell sẽ mở rộng tất cả các mã thông báo trong biểu thức để mở rộng tham số, thay thế lệnh và xóa trích dẫn.

    vì vậy hành vi này được yêu cầu rõ ràng. Điều đó có nghĩa là echo "abc $((4 "*" 5))"số học, thay vì toàn cầu.

    Lưu ý rằng mặc dù $[ ... ]mở rộng số học kiểu cũ không được xử lý theo cùng một cách: dấu ngoặc kép sẽ là một lỗi nếu chúng xuất hiện, bất kể việc mở rộng có được trích dẫn hay không. Hình thức này không còn được ghi nhận nữa và dù sao cũng không được sử dụng.

  • Bản dịch cụ thể theo địa phương với$"..." , thực sự sử dụng "như là một yếu tố cốt lõi. $"được coi là một đơn vị duy nhất.

Có một trường hợp lồng nhau nữa mà bạn có thể không mong đợi, không liên quan đến dấu ngoặc kép, đó là mở rộng cú đúp : {a,b{c,d},e}mở rộng thành "a bc bd e". ${x:-a{b,c}d}thực hiện không tổ, tuy nhiên; nó được coi là sự thay thế tham số cho " a{b,c", theo sau là " d}". Điều đó cũng được ghi lại :

Khi sử dụng dấu ngoặc nhọn, dấu ngoặc kết thúc khớp là '}' đầu tiên không được thoát bằng dấu gạch chéo ngược hoặc trong chuỗi được trích dẫn và không nằm trong phần mở rộng số học được nhúng, thay thế lệnh hoặc mở rộng tham số.


Theo nguyên tắc chung, tất cả các cấu trúc phân định phân tách cơ thể của chúng độc lập với bối cảnh xung quanh (và các trường hợp ngoại lệ được coi là lỗi ). Về bản chất, khi nhìn thấy $(mã thay thế lệnh, chỉ yêu cầu trình phân tích cú pháp tiêu thụ những gì nó có thể từ cơ thể như thể đó là một chương trình mới, và sau đó kiểm tra xem dấu chấm dứt dự kiến ​​(không thoát )hoặc ))hoặc }) xuất hiện khi trình phân tích cú pháp phụ chạy trong số những thứ nó có thể tiêu thụ.

Nếu bạn nghĩ về chức năng của một trình phân tích cú pháp gốc đệ quy , đó chỉ là một đệ quy đơn giản cho trường hợp cơ sở. Cách thực sự dễ thực hiện hơn so với cách khác, một khi bạn đã có nội suy chuỗi. Bất kể kỹ thuật phân tích cú pháp cơ bản, các shell hỗ trợ các cấu trúc này đều cho kết quả tương tự.

Bạn có thể lồng trích dẫn sâu như bạn muốn thông qua các cấu trúc này và nó sẽ hoạt động như mong đợi. Không nơi nào sẽ bị lẫn lộn khi nhìn thấy một trích dẫn ở giữa; thay vào đó, đó sẽ là sự khởi đầu của một chuỗi trích dẫn mới trong bối cảnh bên trong.


Cảm ơn. Trong "blah/blah\n$(cat "${tmpdir}/${filename}.jpdf")", tại sao trích dẫn kép thứ hai không phải là kết thúc của trích dẫn kép đầu tiên (như được hiển thị bằng cú pháp tô sáng trong câu trả lời của bạn), nhưng bắt đầu một chuỗi bên trong $(...)? Có phải vì trình phân tích cú pháp của bash là từ trên xuống, thay vì từ dưới lên?
Tim

2
Có rất nhiều biến thể trong việc xử lý "${var-"foo"}"( echo "${-+"*"}"giống như echo *trong vỏ Bourne hoặc Korn chẳng hạn) và hành vi sẽ được thực hiện rõ ràng không xác định trong phiên bản tiếp theo của tiêu chuẩn . Xem thêm thảo luận tại mail-archive.com/austin-group-l@opengroup.org/msg00167.html
Stéphane Chazelas

3

Có lẽ nhìn vào hai ví dụ với printf(thay vì echo) sẽ giúp:

$ printf '<%s> ' "(echo " * ")"; echo
<(echo > <test.txt> <ppcg.sh> <file1> <file2> <file3> <)>

Nó in (echo (từ đầu tiên, bao gồm cả dấu cách), một số tệp và từ đóng ).
Dấu ngoặc đơn chỉ là một phần của chuỗi trích dẫn (echo .
Dấu hoa thị (hiện không được trích dẫn, vì hai dấu ngoặc kép được ghép nối) được mở rộng dưới dạng toàn cầu thành một danh sách các tệp phù hợp.
Và sau đó, dấu ngoặc đơn đóng.

Tuy nhiên, lệnh thứ hai của bạn hoạt động như sau:

$ printf '<%s> ' "$(echo " * ")" ; echo
< * >

Việc $bắt đầu thay thế lệnh. Điều đó bắt đầu một tươi mới trích dẫn.
Dấu hoa thị được trích dẫn " * "và đó là những gì lệnh (ở đây là lệnh chứ không phải chuỗi được trích dẫn) echoxuất ra. Cuối cùng, printfđịnh dạng lại *và in nó dưới dạng < * >.

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.