Lý do cho cú pháp lạ của câu lệnh "case" trong tập lệnh bash/zsh là gì?


37

Nhìn từ quan điểm của một lập trình viên thì kịch bản lệnh shell chỉ là một ngôn ngữ lập trình khác, trong đó người ta phải học và tuân thủ các quy tắc của ngôn ngữ. Tuy nhiên, tôi phải thừa nhận rằng cú pháp này là kiểu lạ nhất mà tôi từng thấy trong một ngôn ngữ khá phổ biến. Vỏ có lấy cú pháp này từ một ngôn ngữ cũ hơn mà nó descents từ? Có một ý nghĩa đặc biệt/ý nghĩa trong cú pháp?

Như một ví dụ, đây là một đoạn nhỏ mà ta sẽ cất khỏi another post trên SO

case "$1" in 
    start) 
     start 
     ;; 
    stop) 
     stop 
     ;; 
    restart) 
     stop 
     start 
     ;; 
    status) 
     check_status 
     ;; 
    *) 
     echo "Usage: $0 {start|stop|restart|status}" 
     exit 1 
     ;; 
esac 

Nhìn này, trước hết tôi có thể thấy rằng case kết thúc với esac, đó là hình thức đảo ngược của nó (như if kết thúc trong fi). Thứ hai, tôi hiểu rằng mỗi trường hợp được theo sau là ). Đủ công bằng, nhưng tại sao tôi cần hai số ; ở cuối mỗi câu? Tôi cũng sẽ nói rằng ) mà không kèm theo ( là xấu.

Tôi đang tìm thêm thông tin về khía cạnh lịch sử của ngôn ngữ, nhưng tôi cũng đang mở vì lý do kỹ thuật.

+2

Vì vậy, bạn có thể đoán tại sao một vòng lặp là 'cho ...; làm ...; thực hiện 'và không' cho ...; làm ...; od '? Có một lý do chính xác cho nó - nhưng từ khóa đảo ngược giống như Algol để đánh dấu sự kết thúc đã được sử dụng ở nơi khác. 21 nov. 102010-11-21 17:43:53

  0

@ Jonathan Tôi không tranh luận "tại sao nó như thế này và không phải như thế?" (Tôi nghĩ rằng tôi đã làm điều này rõ ràng ngay từ đầu). Câu hỏi của tôi giống như "Cú pháp xấu xí đến từ đâu?" 22 nov. 102010-11-22 12:14:32

+4

Cú pháp đến từ Bourne (của Bourne shell fame). Ông đã làm việc trên Algol, và thích nó đủ để mô hình hóa một số cú pháp vỏ trên Algol. Algol sử dụng các từ khóa đảo ngược để đánh dấu các đầu của các cấu trúc, vì vậy 'case ... esac' là thích hợp. Lý do mà các vòng lặp không kết thúc bằng 'od' là đã có một lệnh 'od' trong kết xuất Unix - bát phân. Vì vậy, 'thực hiện' được sử dụng thay thế. (Và, bằng danh tiếng, mã nguồn vỏ Bourne được viết bằng cách riêng biệt với các macro để làm cho nó trông giống như Algol. Điều này làm cho nó khó duy trì.) 22 nov. 102010-11-22 12:29:57

  0

@ Jonathan tốt nhất, hãy làm cho nó một câu trả lời :) 22 nov. 102010-11-22 16:10:08

27

Theo yêu cầu:

  • Vì vậy, bạn có thể đoán lý do tại sao một vòng lặp là 'for ...; do ...; done' chứ không phải 'for ...; do ...; od'? Có một lý do chính xác cho nó - nhưng từ khóa đảo ngược giống như Algol để đánh dấu sự kết thúc đã được sử dụng ở nơi khác.

Trả lời:

  • Cú pháp đến từ Bourne (của Bourne shell nổi tiếng). Ông đã làm việc trên Algol, và thích nó đủ để mô hình hóa một số cú pháp vỏ trên Algol. Algol sử dụng các từ khóa đảo ngược để đánh dấu các đầu của các cấu trúc, vì vậy 'trường hợp ...esac 'là thích hợp. Lý do mà các vòng lặp không kết thúc bằng 'od' là đã có một lệnh 'od' trong kết xuất Unix - bát phân. Vì vậy, 'thực hiện' được sử dụng thay thế.

Theo danh tiếng, mã nguồn vỏ Bourne được viết bằng riêng biệt C có macro để làm cho nó trông giống như Algol. Điều này làm cho nó khó khăn để duy trì.

Đối với câu hỏi chính - về lý do tại sao không có dấu ngoặc mở (dấu ngoặc đơn) xung quanh các lựa chọn thay thế trong câu lệnh case - Tôi có một vài lý thuyết liên quan.

Trước hết, khi vỏ Bourne được viết (cuối những năm 1970), nhiều chỉnh sửa được thực hiện với 'ed', standard text editor. Nó không có khái niệm bỏ qua một dấu ngoặc đơn cân bằng hoặc các ký hiệu như vậy, vì vậy không có yêu cầu cho một dấu ngoặc đơn hàng đầu. Ngoài ra, nếu bạn đang viết một tài liệu, bạn có thể cũng sắp xếp luận cứ của bạn với:

a) ...blah... 
b) ...more... 
c) ...again... 

Các dấu mở ngoặc thường bỏ qua - và báo cáo kết quả case sẽ phù hợp với mô hình khá hạnh phúc. Tất nhiên, kể từ đó, chúng tôi đã phát triển được sử dụng cho các biên tập viên đánh dấu dấu ngoặc đơn mở phù hợp khi bạn nhập dấu ngoặc đơn gần, vì vậy ký hiệu shell Bourne cũ là một mối phiền toái. Tiêu chuẩn POSIX làm cho dấu ngoặc đơn hàng đầu không bắt buộc; triển khai hiện đại nhất của vỏ giống POSIX (Korn, Bash, Zsh) sẽ hỗ trợ điều đó, và tôi thường sử dụng nó khi tôi không phải lo lắng về tính di động đối với các máy như Solaris 10 nơi/bin/sh vẫn là Bourne trung thành vỏ không cho phép dấu ngoặc đơn hàng đầu. (Tôi thường đối phó với điều đó bằng cách sử dụng #!/bin/ksh làm shebang.)

+1

Tôi muốn nói "câu hỏi chính" là nguồn gốc của cú pháp, là Algol (không thể tìm thấy nó trên trang Wikipedia để bash). Câu trả lời tốt anyway :) 23 nov. 102010-11-23 03:41:07

+1

Thông tin về vỏ Bourne 'nguyên bản' có thể được tìm thấy tại dự án [Gia truyền Bourne Shell] (http://heirloom.sourceforge.net/sh.html) tại Sourceforge (và các địa điểm khác có thể tìm thấy với mục ưa thích của bạn máy tìm kiếm). 23 aug. 132013-08-23 22:44:46


16

Lý do của việc sử dụng ;; là một đơn ; có thể được sử dụng để viết nhiều câu lệnh trong một dòng, như:

restart) 
    stop; start;; 
... 

9

Bash có thể chấp nhận ngoặc phù hợp:

case "$1" in 
    (start) 
     start 
     ;; 
    (stop) 
     stop 
     ;; 

    etc. 
+1

POSIX và Korn vỏ cũng cho phép dấu ngoặc đơn đầu tiên. Đã có một thời gian dài trước đây, 'ed' ('trình soạn thảo văn bản chuẩn' - nó được nói trong sách hướng dẫn UNIX Edition 7) không có tùy chọn 'trả về con trỏ để khớp với dấu ngoặc đơn mở', vì vậy dấu ngoặc đơn mở không thực sự cần thiết. 21 nov. 102010-11-21 17:52:49

  0

Autoconf và bạn bè thích đặt một chú thích với số lượng mở ngoặc thích hợp sau 'in' (ví dụ' case "$ 1" trong # (('). Điều này giúp các shell không thể xử lý câu lệnh case bình thường bên trong' $() 'và biên tập viên giống như cú pháp mới hơn, nhưng cũng hoạt động với vỏ Bourne ban đầu 21 nov. 102010-11-21 22:18:47

  0

@Dennis rất hay để biết, nhưng đó là trước hoặc sau kiểu không phù hợp đã trở nên phổ biến? 22 nov. 102010-11-22 12:24:48

  0

@phunehehe: Tôi không biết ý bạn là gì trừ khi bạn muốn biết liệu có bao giờ một shell có 'case' /' esac' không chấp nhận dấu ngoặc đơn mở hay không. Câu trả lời cho điều đó là "có". Vỏ Bourne ban đầu là một ví dụ. 22 nov. 102010-11-22 12:33:27

  0

Không, ý tôi là, bash chấp nhận kết hợp dấu ngoặc đơn từ đầu của nó? Hoặc đã làm điều đó đến như là một tính năng sau này khi ai đó than phiền về sự xấu xí? 22 nov. 102010-11-22 16:12:21

  0

@phunehehe: Các tài liệu cho điều này đã thay đổi đôi khi giữa 2.0 (1996) và 2.05b (20 02). Tôi không biết liệu nó có được chấp nhận trước khi nó được ghi chép hay không. [CHANGES] (http://tiswww.case.edu/php/chet/bash/CHANGES) dường như im lặng về chủ đề này. 22 nov. 102010-11-22 17:54:19


3

Việc đóng cửa ngoặc đôi khi được sử dụng trong danh sách bằng ngôn ngữ tự nhiên, như

1) do this 
2) do that 

Từ khóa được đảo ngược được lấy từ một số dạng Algol nhưng thực chất là một ý tưởng rất tốt cho việc sử dụng tương tác. Họ phân định rõ ràng phần cuối của một công trình, bao gồm cả nếu có.

Ví dụ, với một cú pháp C-như, sau này đã được phân tích cú pháp:

if (condition) 
    command here; 

là có một else sắp tới hay không? rc, một vỏ từ Kế hoạch 9 với một cú pháp C-như nhiều hơn, giải quyết điều này bằng cách cung cấp if not thay vì else nhưng nó không phải là khá.

Với cú pháp vỏ Bourne, bạn sẽ có else hoặc fi và không cần đọc thêm đầu vào.

  0

Tôi thấy điểm của bạn về danh sách, nhưng phần khác không có ý nghĩa nhiều. Hoặc là đầu vào bổ sung phải được đọc để xác định nếu nó là 'else' hoặc' fi' 22 nov. 102010-11-22 12:22:23

  0

Nhưng đầu vào bổ sung đó là một phần của 'if'. Với cú pháp C-like với 'else', nếu không có' else' shell kết thúc đọc lệnh tiếp theo, điều này khá bất ngờ trong một trình bao tương tác. 22 nov. 102010-11-22 22:30:04