Tại sao không có một bản;


28

Tại sao không có ;ký tự sau docác vòng lặp shell khi được viết trên một dòng?

Ý tôi là đây. Khi được viết trên nhiều dòng, một forvòng lặp trông giống như:

$ for i in $(jot 2)
> do
>     echo $i
> done

Và trên một dòng duy nhất:

$ for i in $(jot 2); do echo $i; done

Tất cả các dòng sụp đổ được một ;sau khi họ ngoại trừ cho các dodòng, và nếu bạn bao gồm ;, nó là một lỗi. Ai đó có lẽ là một người thông minh hơn tôi rất nhiều đã quyết định rằng đây là điều đúng đắn để làm vì một lý do, nhưng tôi không thể hiểu lý do là gì. Có vẻ như không phù hợp với tôi.

Tương tự với whilecác vòng lặp quá.

$ while something
> do
>     anotherthing
> done
$ while something; do anotherthing; done


2
Nó phù hợp: không có dấu chấm phẩy sau while/ formột trong hai.
Dmitry Grigoryev

Điều ngay lập tức xuất hiện trong đầu là nó không cần thiết. Ở những nơi khác, dấu chấm phẩy phân biệt các câu.
Paul Draper

Bởi vì nó sẽ là dư thừa. Không có sự mơ hồ mà không có nó.
dùng207421

Câu trả lời:


29

Đó là cú pháp của lệnh. Xem các lệnh ghép

for name [ [in [words …] ] ; ] do commands; done

Lưu ý cụ thể: do commands

Hầu hết mọi người đặt docommandstrên một dòng riêng biệt để cho phép dễ đọc hơn nhưng không cần thiết, bạn có thể viết:

for i in thing
do something
done

Tôi biết câu hỏi này là cụ thể về shell và tôi đã liên kết với hướng dẫn bash. Nó không được viết theo cách đó trong sổ tay shell nhưng nó được viết theo cách đó trong một bài viết được viết bởi Stephen Bourne cho tạp chí byte.

Stephen nói:

Một danh sách lệnh là một chuỗi của một hoặc đơn giản hơn lệnh tách hoặc chấm dứt bởi một dòng mới hoặc ;(dấu chấm phẩy). Hơn nữa, các từ dành riêng như làmthực hiện thường trước bởi một dòng mới hoặc ;... Trong lần lượt mỗi lần danh sách lệnh sau làm được thực thi.


5
Nó cũng thú vị tại sao không phải có dấu chấm phẩy, như thế for i in thing; do; something; done. Hoàn toàn ngắt dòng được cho phép, dấu chấm phẩy không được phép.
rexkogitans

@gidds: vâng, chính xác. Đó cũng là cách cấu trúc ngữ pháp shell. Đây somethinglà chủ đề và doáp dụng cho nó. Giống như trong cấu trúc câu tiếng Anh.
Peter Cordes

@gidds Một nghĩa đen (hoặc một số cú pháp khác) là cần thiết bởi vì nhiều lệnh có thể đi trong điều kiện (trong whilecú pháp), vì vậy bạn cần một cái gì đó để phân biệt các lệnh điều kiện với các lệnh thân vòng lặp. Sử dụng dođể tách chúng có lẽ là những gì đơn giản là phù hợp nhất.
JoL

@rexkogitans Thật vậy, tôi chưa bao giờ nhận ra đó là lỗi cú pháp. Câu hỏi tiêu đề này rất phù hợp cho điều đó, quá. Nó có lẽ phải làm với việc không cho phép một cơ thể trống rỗng. Tuy nhiên, bạn nhận được một lỗi cú pháp hơi khác nhau khi tạo một nội dung trống với các dòng mới và ví dụ của bạn không có phần thân trống.
JoL

@rexkogitans Dấu chấm phẩy ở đây là một phần của cú pháp vòng lặp và khác biệt với vai trò khác của nó như là một đầu cuối danh sách lệnh. Dòng mới là khác nhau bởi vì nếu nó không được yêu cầu hoặc mong đợi, nó được coi như khoảng trắng thông thường. Shell ngữ pháp là lộn xộn.
chepner

12

Tôi không biết lý do ban đầu của cú pháp này là gì, nhưng hãy xem xét thực tế rằng whilecác vòng lặp có thể nhận nhiều lệnh trong phần điều kiện, ví dụ:

while a=$(somecmd);
      [ -n "$a" ];
do
    echo "$a"
done

Ở đây, dotừ khóa là cần thiết để phân biệt phần điều kiện và phần chính của vòng lặp.dolà một từ khóa như while, ifthen(và done), và nó dường như phù hợp với các từ khóa khác mà nó không yêu cầu dấu chấm phẩy hoặc dòng mới sau nó nhưng xuất hiện ngay trước lệnh.

Sự thay thế (khái quát đến ifwhile) sẽ trông hơi xấu xí, chúng ta cần phải viết ví dụ

if; [ -n "$a" ]; then
    some command here
fi

Cú pháp của forcác vòng lặp là tương tự nhau.


11

Cú pháp Shell là tiền tố dựa. Nó có các mệnh đề được giới thiệu bởi các từ khóa đặc biệt. Một số mệnh đề phải đi cùng nhau.

Một whilevòng lặp được tạo ra một hoặc nhiều lệnh kiểm tra:

test ; test ; test ; ...

và bằng một hoặc nhiều lệnh cơ thể:

body ; body ; body ; ...

Một cái gì đó phải nói với shell rằng một vòng lặp while bắt đầu. Đó là mục đích của whiletừ này:

while test ; test ; test ; ...

Nhưng sau đó, mọi thứ mơ hồ. Lệnh nào là bắt đầu của cơ thể? Một cái gì đó phải chỉ ra điều đó, và đó là những gì dotiền tố làm:

do body ; body ; body ; ...

và cuối cùng, một cái gì đó phải chỉ ra rằng cơ thể cuối cùng đã được nhìn thấy; một từ khóa đặc biệt donelàm điều đó.

Các từ khóa shell này không yêu cầu phân tách dấu chấm phẩy, ngay cả trên cùng một dòng. Ví dụ, nếu bạn đóng một số vòng lặp lồng nhau, bạn có thể có done done done ....

Thay vào đó, dấu chấm phẩy nằm giữa ... test ; body ... nếu chúng nằm trên cùng một dòng. Dấu chấm phẩy đó được hiểu là một dấu chấm hết: nó thuộc về test. Do đó, nếu một dotừ khóa được chèn giữa chúng, nó phải đi giữa dấu chấm phẩy và body. Nếu nó ở phía bên kia của dấu chấm phẩy, nó sẽ bị nhúng sai vào bên trongtest cú pháp của lệnh, thay vì được đặt giữa các lệnh.

Cú pháp shell ban đầu được thiết kế bởi Stephen Bourne và được lấy cảm hứng từ Algol . Bourne yêu Algol đến nỗi anh ta đã sử dụng rất nhiều macro C trong mã nguồn shell để làm cho C trông giống Algol. Bạn có thể duyệt các nguồn shell có từ năm 1979 từ Phiên bản Unix 7 . Các macro đang ở mac.hvà chúng được sử dụng ở mọi nơi. Ví dụ ifbáo cáo được trả lại như IF... ELSE... ELIF... FI.


Mã nguồn rất ấn tượng.
keithpjcar

Vâng, đó là một chuyến tham quan của cpp.
đánh cắp

7

Tôi cho rằng điều đó dokhông đặc biệt ở đây. Bạn gặp lỗi vì ;không thể bắt đầu danh sách lệnh. Nếu có, lệnh đầu tiên sẽ trống và ngữ pháp không cho phép các lệnh trống (bài tập biến đổi hoặc chuyển hướng mà không có lệnh, có, nhưng hoàn toàn không có gì, không). Điều này đúng ở bất kỳ nơi nào, bất cứ nơi nào có danh sách các lệnh được mong đợi:

$ while true; do ; echo foo; done
dash: 1: Syntax error: ";" unexpected
$ while ; true; do; echo; done
dash: 1: Syntax error: ";" unexpected
$ { ; echo foo; }
dash: 1: Syntax error: ";" unexpected
$ if ; true; then echo foo; fi
dash: 1: Syntax error: ";" unexpected

Cũng:

$ ; ;
dash: 1: Syntax error: ";" unexpected

Trong tất cả những nơi này, một danh sách lệnh được mong đợi, và do đó, một sự dẫn đầu ;là bất ngờ.


Vâng - cách tôi tự ý thêm một '\ n' sau 'do' khiến tôi nghĩ rằng 'do' là một mã thông báo độc lập và không phải là thứ hoạt động trên khối theo sau.
keithpjcar

Làm thế nào mà một dòng mới không thoát (mặc dù có vẻ như hoạt động giống như một dấu chấm phẩy trong cú pháp shell) được cho phép sau do(và ifvv), mặc dù vậy?
Henning Makholm

@Henning Bất kỳ số lượng dòng mới được cho phép trước (và sau) một danh sách lệnh. Dòng mới và dấu chấm phẩy cũng hành xử khác nhau trong các khía cạnh khác. Ví dụ, mở rộng bí danh được thực hiện khi toàn bộ dòng đầu vào được đọc (nghĩa là lên đến dòng mới tiếp theo) và không phải cho mỗi lệnh.
muru

3

Cú pháp là:

for i in [whitespace separated list of values]
do [newline separated list of commands]
done

Bất kỳ dòng mới nào, trong danh sách lệnh hoặc trước "làm" hoặc "thực hiện" đều có thể được thay thế bằng ";".

Một dòng mới (nhưng không phải là ";") được cho phép sau "làm" và trước "trong", chỉ để làm cho mọi thứ đẹp hơn, nhưng sẽ không có ý nghĩa khi yêu cầu một dòng mới ở đó.


3

if tương tự với các từ khóa của nó: điều này khá dễ đọc khi các lệnh ngắn:

if   test_commands
then true_commands
else false_commands
fi

Một khi tôi nhận ra rằng tôi đã đặt một cách tùy tiện \nsau khi dotất cả đều có ý nghĩa (miễn là bạn không nghĩ về việc không thể đặt một cách tùy ý \ngiữa các lệnh và đối số khác).
keithpjcar

Vâng, do, then, elsekhông args - nếu họ, bạn sẽ không được phép sử dụng một dấu chấm phẩy trước mặt họ. Chúng là các từ khóa shell (xem type if then elif else fi)
glenn jackman

Tôi đoán tôi đã không rõ ràng như tôi muốn được trả lời.
keithpjcar

Quan điểm của tôi là "làm" không giống như bất kỳ "lệnh khác". Vì vậy, nó tuân theo các quy tắc khác nhau.
glenn jackman

3

Câu trả lời ngắn, bởi vì điều này được chỉ định bởi ngữ pháp shell POSIX . Bất cứ điều gì giữa dodoneđược coi là một nhóm các câu lệnh, trong khi ;là dấu phân cách tuần tự:

for_clause       : For name                                      do_group
                 | For name                       sequential_sep do_group
                 | For name linebreak in          sequential_sep do_group
                 | For name linebreak in wordlist sequential_sep do_group

do_group         : Do compound_list Done 

sequential_sep   : ';' linebreak
                 | newline_list

Ngoài ra, nếu ;cần thiết, tiêu chuẩn sẽ nêu rõ và sẽ bao gồm sequential_sepsau Do.


1
@drewbenn Ý bạn là cái đầu tiên? Đó là lặp đi lặp lại thông qua các tham số vị trí. Vì vậy, nếu bạn có một tập lệnh được gọi là ./myscript.sh abc, trong đó abc là đối số, vòng lặp cho i; làm tiếng vang "$ i"; thực hiện sẽ lặp qua abc, mặc dù tôi vẫn nghĩ rằng nó sẽ cần dấu chấm phẩy để nó hoạt động
Sergiy Kolodyazhnyy

Ok, vậy là những nghi ngờ của tôi cũng được xóa tan
Sergiy Kolodyazhnyy

1

Bởi vì do là một từ khóa và những gì tiếp theo nó là các tham số cơ bản. Thông dịch viên Bash biết rằng nhiều hơn sau. Nó tương tự như cách không có ';' theo i trong lệnh for. Bạn thực sự có thể thêm một dòng mới sau i in và nhập phần còn lại trên một dòng mới và nó sẽ hoạt động tốt.

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.