Chúng tôi đã thực hiện các thí nghiệm để điều tra ngữ pháp của các tập lệnh bó. Chúng tôi cũng đã nghiên cứu sự khác biệt giữa chế độ dòng lệnh và dòng lệnh.
Trình phân tích cú pháp hàng loạt:
Dưới đây là tổng quan ngắn gọn về các giai đoạn trong trình phân tích cú pháp dòng tệp bó:
Giai đoạn 0) Đọc dòng:
Giai đoạn 1) Mở rộng phần trăm:
Giai đoạn 2) Xử lý các ký tự đặc biệt, mã thông báo và xây dựng khối lệnh được lưu trong bộ nhớ cache: Đây là một quy trình phức tạp bị ảnh hưởng bởi những thứ như dấu ngoặc kép, ký tự đặc biệt, dấu phân cách mã thông báo và thoát dấu mũ.
Giai đoạn 3) Báo lại (các) lệnh được phân tích cú pháp Chỉ khi khối lệnh không bắt đầu @
và ECHO được BẬT khi bắt đầu bước trước.
Giai đoạn 4) %X
Mở rộng biến FOR : Chỉ khi lệnh FOR hoạt động và các lệnh sau khi DO đang được xử lý.
Giai đoạn 5) Mở rộng bị trì hoãn: Chỉ khi bật mở rộng bị trì hoãn
Giai đoạn 5.3) Xử lý đường ống: Chỉ khi các lệnh nằm ở hai bên của đường ống
Giai đoạn 5.5) Thực hiện chuyển hướng:
Giai đoạn 6) Xử lý CALL / Nhân đôi Caret: Chỉ khi mã thông báo lệnh là GỌI
Giai đoạn 7) Thực thi: Lệnh được thực thi
Dưới đây là chi tiết cho từng giai đoạn:
Lưu ý rằng các giai đoạn được mô tả dưới đây chỉ là một mô hình về cách hoạt động của trình phân tích cú pháp hàng loạt. Các nội bộ cmd.exe thực tế có thể không phản ánh các giai đoạn này. Nhưng mô hình này có hiệu quả trong việc dự đoán hành vi của các tập lệnh bó.
Giai đoạn 0) Dòng đọc: Đọc dòng đầu vào qua đầu tiên <LF>
.
- Khi đọc một dòng được phân tích cú pháp dưới dạng lệnh,
<Ctrl-Z>
(0x1A) được đọc là <LF>
(LineFeed 0x0A)
- Khi GOTO hoặc CALL đọc các dòng trong khi quét nhãn :
<Ctrl-Z>
, được coi là chính nó - nó không được chuyển đổi thành<LF>
Giai đoạn 1) Mở rộng phần trăm:
- Một đôi
%%
được thay thế bằng một%
- Mở rộng đối số (
%*
, %1
, %2
, vv)
- Mở rộng
%var%
, nếu var không tồn tại thay thế nó bằng không có gì
- Dòng đầu tiên bị cắt ngắn
<LF>
không nằm trong phạm vi %var%
mở rộng
- Để được giải thích đầy đủ, hãy đọc nửa đầu của phần này từ dbenham Cùng một chủ đề: Phần trăm pha
Giai đoạn 2) Xử lý các ký tự đặc biệt, mã thông báo và xây dựng khối lệnh được lưu trong bộ nhớ cache: Đây là một quy trình phức tạp bị ảnh hưởng bởi những thứ như dấu ngoặc kép, ký tự đặc biệt, dấu phân cách mã thông báo và thoát dấu mũ. Những gì sau đây là một xấp xỉ của quá trình này.
Có những khái niệm quan trọng trong suốt giai đoạn này.
- Mã thông báo đơn giản là một chuỗi các ký tự được coi là một đơn vị.
- Mã thông báo được phân tách bằng dấu phân cách mã thông báo. Các dấu phân cách mã thông báo tiêu chuẩn là
<space>
<tab>
;
,
=
<0x0B>
<0x0C>
và các <0xFF>
dấu phân cách mã thông báo liên tiếp được coi là một - không có mã thông báo trống giữa các dấu phân cách mã thông báo
- Không có dấu phân cách mã thông báo trong một chuỗi được trích dẫn. Toàn bộ chuỗi trích dẫn luôn được coi là một phần của một mã thông báo. Một mã thông báo duy nhất có thể bao gồm sự kết hợp của các chuỗi được trích dẫn và các ký tự không được trích dẫn.
Các ký tự sau có thể có ý nghĩa đặc biệt trong giai đoạn này, tùy thuộc vào ngữ cảnh: <CR>
^
(
@
&
|
<
>
<LF>
<space>
<tab>
;
,
=
<0x0B>
<0x0C>
<0xFF>
Nhìn vào từng nhân vật từ trái sang phải:
- Nếu
<CR>
sau đó loại bỏ nó, như thể nó không bao giờ ở đó (ngoại trừ hành vi chuyển hướng kỳ lạ )
- Nếu một dấu mũ (
^
), ký tự tiếp theo được thoát và dấu mũ thoát được loại bỏ. Nhân vật trốn thoát mất hết ý nghĩa đặc biệt (trừ <LF>
).
- Nếu một trích dẫn (
"
), hãy chuyển cờ báo giá. Nếu cờ trích dẫn đang hoạt động, thì chỉ "
và <LF>
là đặc biệt. Tất cả các nhân vật khác mất ý nghĩa đặc biệt của họ cho đến khi trích dẫn tiếp theo tắt cờ trích dẫn. Không thể thoát khỏi trích dẫn kết thúc. Tất cả các ký tự được trích dẫn luôn nằm trong cùng một mã thông báo.
<LF>
luôn luôn tắt cờ báo giá. Các hành vi khác thay đổi tùy theo ngữ cảnh, nhưng trích dẫn không bao giờ thay đổi hành vi của <LF>
.
- Đã trốn thoát
<LF>
<LF>
bị tước
- Nhân vật tiếp theo được trốn thoát. Nếu ở cuối bộ đệm dòng, thì dòng tiếp theo được đọc và xử lý bởi các pha 1 và 1.5 và được thêm vào dòng hiện tại trước khi thoát ký tự tiếp theo. Nếu ký tự tiếp theo là
<LF>
, thì nó được coi là một nghĩa đen, có nghĩa là quá trình này không được đệ quy.
- Unescaped
<LF>
không nằm trong ngoặc đơn
<LF>
bị tước và phân tích cú pháp của dòng hiện tại bị chấm dứt.
- Bất kỳ ký tự còn lại trong bộ đệm dòng chỉ đơn giản là bỏ qua.
- Không được giải thoát
<LF>
trong một khối ngoặc đơn FOR IN
<LF>
được chuyển đổi thành một <space>
- Nếu ở cuối bộ đệm dòng, thì dòng tiếp theo được đọc và gắn vào dòng hiện tại.
- Không thoát
<LF>
trong khối lệnh được ngoặc đơn
<LF>
được chuyển đổi thành <LF><space>
và <space>
được coi là một phần của dòng tiếp theo của khối lệnh.
- Nếu ở cuối bộ đệm dòng, thì dòng tiếp theo được đọc và gắn vào khoảng trắng.
- Nếu một trong các ký tự đặc biệt
&
|
<
hoặc >
, chia dòng tại thời điểm này để xử lý các đường ống, nối lệnh và chuyển hướng.
- Trong trường hợp đường ống (
|
), mỗi bên là một lệnh (hoặc khối lệnh) riêng biệt được xử lý đặc biệt trong giai đoạn 5.3
- Trong trường hợp
&
, &&
hoặc ||
nối lệnh, mỗi bên của nối được coi là một lệnh riêng biệt.
- Trong trường hợp
<
, <<
, >
, hoặc >>
chuyển hướng, mệnh đề chuyển hướng được phân tách, tạm thời loại bỏ, và sau đó nối vào cuối của lệnh hiện hành. Mệnh đề chuyển hướng bao gồm một chữ số xử lý tệp tùy chọn, toán tử chuyển hướng và mã thông báo đích chuyển hướng.
- Nếu mã thông báo đi trước toán tử chuyển hướng là một chữ số không thoát, thì chữ số chỉ định xử lý tệp sẽ được chuyển hướng. Nếu không tìm thấy mã thông báo xử lý, thì đầu ra chuyển hướng mặc định thành 1 (stdout) và chuyển hướng đầu vào mặc định thành 0 (stdin).
- Nếu mã thông báo đầu tiên cho lệnh này (trước khi chuyển hướng chuyển đến cuối) bắt đầu bằng
@
, thì nó @
có ý nghĩa đặc biệt. ( @
không đặc biệt trong bất kỳ bối cảnh nào khác)
- Sự đặc biệt
@
được loại bỏ.
- Nếu ECHO đang BẬT, thì lệnh này, cùng với bất kỳ lệnh được nối nào sau đây trên dòng này, được loại trừ khỏi tiếng vang giai đoạn 3. Nếu
@
là trước khi mở (
, thì toàn bộ khối ngoặc được loại trừ khỏi tiếng vang giai đoạn 3.
- Quá trình ngoặc đơn (cung cấp cho các câu lệnh ghép trên nhiều dòng):
- Nếu trình phân tích cú pháp không tìm kiếm mã thông báo lệnh, thì
(
nó không đặc biệt.
- Nếu trình phân tích cú pháp đang tìm kiếm mã thông báo lệnh và tìm thấy
(
, thì hãy bắt đầu một câu lệnh ghép mới và tăng bộ đếm dấu ngoặc đơn
- Nếu bộ đếm dấu ngoặc đơn> 0 thì
)
chấm dứt câu lệnh ghép và giảm bộ đếm dấu ngoặc đơn.
- Nếu kết thúc dòng đạt được và bộ đếm dấu ngoặc đơn> 0 thì dòng tiếp theo sẽ được thêm vào câu lệnh ghép (bắt đầu lại với pha 0)
- Nếu bộ đếm dấu ngoặc đơn là 0 và trình phân tích cú pháp đang tìm kiếm một lệnh, thì các
)
chức năng tương tự như một REM
câu lệnh miễn là nó được theo sau bởi một dấu phân cách mã thông báo, ký tự đặc biệt, dòng mới hoặc cuối tệp
- Tất cả các ký tự đặc biệt mất ý nghĩa của chúng ngoại trừ
^
(nối dòng là có thể)
- Khi kết thúc dòng logic, toàn bộ "lệnh" sẽ bị loại bỏ.
- Mỗi lệnh được phân tích thành một loạt các mã thông báo. Mã thông báo đầu tiên luôn được coi là mã thông báo lệnh (sau khi mã đặc biệt
@
đã bị tước và chuyển hướng đến cuối).
- Các dấu phân cách mã thông báo hàng đầu trước mã thông báo lệnh bị tước
- Khi phân tích mã thông báo lệnh,
(
hoạt động như một dấu phân cách mã thông báo lệnh, ngoài các dấu phân cách mã thông báo tiêu chuẩn
- Việc xử lý các mã thông báo tiếp theo phụ thuộc vào lệnh.
- Hầu hết các lệnh chỉ đơn giản là nối tất cả các đối số sau khi mã thông báo lệnh thành một mã thông báo đối số duy nhất. Tất cả các dấu phân cách mã thông báo đối số được bảo tồn. Các tùy chọn đối số thường không được phân tích cú pháp cho đến giai đoạn 7.
- Ba lệnh được xử lý đặc biệt - IF, FOR và REM
- IF được chia thành hai hoặc ba phần riêng biệt được xử lý độc lập. Một lỗi cú pháp trong cấu trúc IF sẽ dẫn đến lỗi cú pháp nghiêm trọng.
- Hoạt động so sánh là lệnh thực tế chảy suốt đến giai đoạn 7
- Tất cả các tùy chọn IF được phân tích cú pháp đầy đủ trong giai đoạn 2.
- Các dấu phân cách mã thông báo liên tiếp sụp đổ vào một không gian duy nhất.
- Tùy thuộc vào toán tử so sánh, sẽ có một hoặc hai mã thông báo giá trị được xác định.
- Khối lệnh True là tập hợp các lệnh sau điều kiện và được phân tích cú pháp như bất kỳ khối lệnh nào khác. Nếu ELSE được sử dụng, thì khối True phải được ngoặc đơn.
- Khối lệnh Sai tùy chọn là tập hợp các lệnh sau ELSE. Một lần nữa, khối lệnh này được phân tích cú pháp bình thường.
- Các khối lệnh Đúng và Sai không tự động chảy vào các giai đoạn tiếp theo. Quá trình xử lý tiếp theo của họ được kiểm soát bởi giai đoạn 7.
- FOR được chia làm hai sau DO. Một lỗi cú pháp trong cấu trúc FOR sẽ dẫn đến lỗi cú pháp nghiêm trọng.
- Phần qua DO là lệnh lặp FOR thực tế chảy suốt giai đoạn 7
- Tất cả các tùy chọn FOR được phân tích cú pháp đầy đủ trong giai đoạn 2.
- Mệnh đề IN được coi
<LF>
là <space>
. Sau khi mệnh đề IN được phân tích cú pháp, tất cả các mã thông báo được nối với nhau để tạo thành một mã thông báo duy nhất.
- Liên tiếp các dấu phân cách mã thông báo không được trích dẫn / không trích dẫn sụp đổ vào một khoảng trống trong suốt lệnh FOR thông qua DO.
- Phần sau DO là một khối lệnh được phân tích cú pháp bình thường. Việc xử lý tiếp theo của khối lệnh DO được điều khiển bởi phép lặp trong giai đoạn 7.
- REM được phát hiện trong giai đoạn 2 được xử lý khác biệt đáng kể so với tất cả các lệnh khác.
- Chỉ có một mã thông báo đối số được phân tích cú pháp - trình phân tích cú pháp bỏ qua các ký tự sau mã thông báo đối số đầu tiên.
- Lệnh REM có thể xuất hiện ở đầu ra giai đoạn 3, nhưng lệnh không bao giờ được thực thi và văn bản đối số ban đầu được lặp lại - các dấu thoát không bị xóa, ngoại trừ ...
- Nếu chỉ có một mã thông báo đối số kết thúc bằng một mã thông báo không kết
^
thúc dòng, thì mã thông báo đối số sẽ bị loại bỏ và dòng tiếp theo được phân tích cú pháp và gắn vào REM. Điều này lặp lại cho đến khi có nhiều hơn một mã thông báo, hoặc ký tự cuối cùng thì không ^
.
- Nếu mã thông báo lệnh bắt đầu bằng
:
và đây là vòng đầu tiên của giai đoạn 2 (không phải là khởi động lại do CALL trong giai đoạn 6) thì
- Mã thông báo thường được coi là Nhãn chưa được thực hiện .
- Phần còn lại của dòng được phân tách, tuy nhiên
)
, <
, >
, &
và |
không còn có ý nghĩa đặc biệt. Toàn bộ phần còn lại của dòng được coi là một phần của "lệnh" nhãn.
- Các
^
tiếp tục là đặc biệt, có nghĩa là tiếp tục dòng có thể được sử dụng để thêm các dòng sau vào nhãn.
- Một nhãn chưa được thực hiện trong một khối được ngoặc đơn sẽ dẫn đến một lỗi cú pháp nghiêm trọng trừ khi nó được theo sau bởi một lệnh hoặc Nhãn đã thực hiện trên dòng tiếp theo.
(
không còn có ý nghĩa đặc biệt đối với lệnh đầu tiên tuân theo Nhãn chưa được thực hiện .
- Lệnh bị hủy bỏ sau khi phân tích nhãn xong. Các giai đoạn tiếp theo không diễn ra cho nhãn
- Có ba trường hợp ngoại lệ có thể khiến nhãn được tìm thấy trong giai đoạn 2 được coi là Nhãn thực thi tiếp tục phân tích cú pháp qua giai đoạn 7.
- Có chuyển hướng mà đến trước nhãn Token, và có một
|
đường ống hoặc &
, &&
hoặc ||
lệnh nối trên đường dây.
- Có sự chuyển hướng đi trước mã thông báo nhãn và lệnh nằm trong khối được ngoặc đơn.
- Mã thông báo nhãn là lệnh đầu tiên trên một dòng trong khối được ngoặc đơn và dòng trên kết thúc bằng một Nhãn chưa được thực hiện .
- Điều sau đây xảy ra khi Nhãn thực thi được phát hiện trong giai đoạn 2
- Nhãn, đối số và chuyển hướng của nó đều được loại trừ khỏi mọi đầu ra tiếng vang trong giai đoạn 3
- Bất kỳ lệnh nối tiếp theo nào trên dòng đều được phân tích cú pháp và thực thi đầy đủ.
- Để biết thêm thông tin về Nhãn đã thi vs Nhãn chưa thi hành , xem https://www.dostips.com/forum/viewtopic.php?f=3&t=3803&p=55405#p55405
Giai đoạn 3) Báo lại (các) lệnh được phân tích cú pháp Chỉ khi khối lệnh không bắt đầu @
và ECHO được BẬT khi bắt đầu bước trước.
Giai đoạn 4) %X
Mở rộng biến FOR : Chỉ khi lệnh FOR hoạt động và các lệnh sau khi DO đang được xử lý.
- Tại thời điểm này, giai đoạn 1 của xử lý hàng loạt sẽ chuyển đổi một biến FOR như
%%X
thành %X
. Dòng lệnh có các quy tắc mở rộng phần trăm khác nhau cho giai đoạn 1. Đây là lý do mà các dòng lệnh sử dụng %X
nhưng các tệp bó sử dụng %%X
cho các biến FOR.
- Tên biến FOR là trường hợp nhạy cảm, nhưng
~modifiers
không phân biệt chữ hoa chữ thường.
~modifiers
được ưu tiên hơn tên biến. Nếu một ký tự theo sau ~
vừa là tên biến đổi vừa là tên biến FOR hợp lệ và tồn tại một ký tự tiếp theo là tên biến FOR hoạt động, thì ký tự đó được hiểu là biến tố.
- Tên biến FOR là toàn cục, nhưng chỉ trong ngữ cảnh của mệnh đề DO. Nếu một thường trình được GỌI từ trong mệnh đề FOR DO, thì các biến FOR không được mở rộng trong thói quen GỌI. Nhưng nếu thường trình có lệnh FOR riêng, thì tất cả các biến FOR được xác định hiện tại đều có thể truy cập được vào các lệnh DO bên trong.
- Tên biến có thể được sử dụng lại trong các FOR lồng nhau. Giá trị FOR bên trong được ưu tiên, nhưng một khi INNER FOR đóng, thì giá trị FOR bên ngoài được khôi phục.
- Nếu ECHO được BẬT khi bắt đầu giai đoạn này, thì giai đoạn 3) được lặp lại để hiển thị các lệnh DO được phân tích cú pháp sau khi các biến FOR được mở rộng.
---- Từ thời điểm này trở đi, mỗi lệnh được xác định trong giai đoạn 2 được xử lý riêng.
---- Các giai đoạn 5 đến 7 được hoàn thành cho một lệnh trước khi chuyển sang lệnh tiếp theo.
Giai đoạn 5) Mở rộng bị trì hoãn: Chỉ khi mở rộng bị trì hoãn, lệnh không nằm trong khối được ngoặc đơn ở hai bên của ống và lệnh không phải là tập lệnh bó "trần trụi" (tên tập lệnh không có dấu ngoặc đơn, CALL, nối lệnh, hoặc đường ống).
- Mỗi mã thông báo cho một lệnh được phân tích cú pháp để mở rộng chậm trễ một cách độc lập.
- Hầu hết các lệnh phân tích hai hoặc nhiều mã thông báo - mã thông báo lệnh, mã thông báo đối số và mỗi mã thông báo đích chuyển hướng.
- Lệnh FOR chỉ phân tích mã thông báo mệnh đề IN.
- Lệnh IF chỉ phân tích các giá trị so sánh - một hoặc hai, tùy thuộc vào toán tử so sánh.
- Đối với mỗi mã thông báo được phân tích cú pháp, trước tiên hãy kiểm tra xem nó có chứa bất kỳ
!
. Nếu không, mã thông báo không được phân tích cú pháp - quan trọng đối với các ^
ký tự. Nếu mã thông báo có chứa !
, sau đó quét từng ký tự từ trái sang phải:
- Nếu đó là dấu mũ (
^
) ký tự tiếp theo không có ý nghĩa đặc biệt, thì dấu mũ sẽ bị xóa
- Nếu đó là dấu chấm than, hãy tìm kiếm dấu chấm than tiếp theo (dấu mũ không được quan sát nữa), mở rộng thành giá trị của biến.
- Mở liên tiếp
!
được thu gọn thành một!
- Bất kỳ không ghép đôi còn lại
!
được loại bỏ
- Mở rộng các vars ở giai đoạn này là "an toàn", vì các ký tự đặc biệt không được phát hiện nữa (thậm chí
<CR>
hoặc <LF>
)
- Để được giải thích đầy đủ hơn, hãy đọc nửa sau của điều này từ
cùng một chủ đề - Giai đoạn dấu chấm than
Giai đoạn 5.3) Xử lý đường ống: Chỉ khi các lệnh nằm ở hai bên của đường ống
Mỗi bên của đường ống được xử lý độc lập và không đồng bộ.
- Nếu lệnh là nội bộ của cmd.exe, hoặc nó là một tệp bó hoặc nếu đó là một khối lệnh được ngoặc đơn, thì nó được thực thi trong một luồng cmd.exe mới thông qua
%comspec% /S /D /c" commandBlock"
, vì vậy khối lệnh sẽ khởi động lại pha, nhưng lần này trong chế độ dòng lệnh.
- Nếu một khối lệnh được ngoặc đơn, thì tất cả
<LF>
với một lệnh trước và sau được chuyển đổi thành <space>&
. Khác <LF>
bị tước.
- Đây là kết thúc xử lý cho các lệnh ống.
- Xem tại sao việc mở rộng bị trì hoãn thất bại khi bên trong một khối mã được xử lý? để biết thêm về phân tích và xử lý đường ống
Giai đoạn 5.5) Thực hiện chuyển hướng: Bất kỳ chuyển hướng nào được phát hiện trong giai đoạn 2 đều được thực hiện.
Giai đoạn 6) Xử lý CALL / Nhân đôi Caret: Chỉ khi mã thông báo lệnh là GỌI hoặc nếu văn bản trước dấu phân cách mã thông báo tiêu chuẩn xuất hiện đầu tiên là GỌI. Nếu CALL được phân tích cú pháp từ mã thông báo lệnh lớn hơn, thì phần không được sử dụng sẽ được thêm vào mã thông báo đối số trước khi tiếp tục.
- Quét mã thông báo đối số cho một không trích dẫn
/?
. Nếu tìm thấy bất cứ nơi nào trong mã thông báo, sau đó hủy bỏ giai đoạn 6 và chuyển sang Giai đoạn 7, trong đó GIÚP cho CALL sẽ được in.
- Xóa cái đầu tiên
CALL
, để nhiều CALL có thể được xếp chồng lên nhau
- Nhân đôi tất cả
- Khởi động lại các giai đoạn 1, 1.5 và 2, nhưng không tiếp tục giai đoạn 3
- Bất kỳ dấu mũ đôi nào cũng được giảm trở lại thành một dấu mũ miễn là chúng không được trích dẫn. Nhưng thật không may, dấu ngoặc kép vẫn tăng gấp đôi.
- Giai đoạn 1 thay đổi một chút
- Lỗi mở rộng ở bước 1.2 hoặc 1.3 đã hủy bỏ GỌI, nhưng lỗi không nghiêm trọng - quá trình xử lý hàng loạt vẫn tiếp tục.
- Nhiệm vụ giai đoạn 2 được thay đổi một chút
- Bất kỳ chuyển hướng mới nào không được trích dẫn, không được giải mã mà không được phát hiện trong vòng đầu tiên của giai đoạn 2 đều được phát hiện, nhưng nó đã bị xóa (bao gồm cả tên tệp) mà không thực sự thực hiện chuyển hướng
- Bất kỳ dấu mũ mới xuất hiện không được trích dẫn, không thoát ra ở cuối dòng được loại bỏ mà không thực hiện tiếp tục dòng
- CALL bị hủy bỏ mà không có lỗi nếu phát hiện bất kỳ điều nào sau đây
- Mới xuất hiện không được trích dẫn, không được giải thoát
&
hoặc|
- Mã thông báo lệnh kết quả bắt đầu bằng không trích dẫn, không thoát
(
- Mã thông báo đầu tiên sau khi CALL bị xóa bắt đầu bằng
@
- Nếu lệnh kết quả là IF hoặc FOR có vẻ hợp lệ, thì việc thực thi sau đó sẽ thất bại với lỗi cho biết
IF
hoặcFOR
không được công nhận là lệnh nội bộ hoặc bên ngoài.
- Tất nhiên, CALL không bị hủy bỏ trong vòng 2 của giai đoạn 2 này nếu mã thông báo lệnh kết quả là nhãn bắt đầu bằng
:
.
- Nếu mã thông báo lệnh kết quả là CALL, sau đó khởi động lại Giai đoạn 6 (lặp lại cho đến khi không còn GỌI nữa)
- Nếu mã thông báo lệnh kết quả là tập lệnh bó hoặc nhãn: thì việc thực thi CALL được xử lý hoàn toàn bởi phần còn lại của Giai đoạn 6.
- Nhấn vị trí tệp tập lệnh hiện tại trên ngăn xếp cuộc gọi để việc thực thi có thể tiếp tục từ vị trí chính xác khi CALL hoàn tất.
- Thiết lập mã thông báo đối số% 0,% 1,% 2, ...% N và% * cho CALL, sử dụng tất cả các mã thông báo kết quả
- Nếu mã thông báo lệnh là nhãn bắt đầu bằng
:
, thì
- Khởi động lại Giai đoạn 5. Điều này có thể ảnh hưởng đến những gì: nhãn được GỌI. Nhưng vì mã thông báo% 0, v.v. đã được thiết lập, nên nó sẽ không thay đổi các đối số được truyền cho thói quen GỌI.
- Thực thi nhãn GOTO để định vị con trỏ tệp ở đầu chương trình con (bỏ qua mọi mã thông báo khác có thể theo nhãn:) Xem Giai đoạn 7 để biết các quy tắc về cách GOTO hoạt động.
- Khác kiểm soát chuyển giao cho tập lệnh bó được chỉ định.
- Việc thực thi nhãn CALLed: script hoặc script tiếp tục cho đến khi đạt được EXIT / B hoặc end-of-file, tại đó, ngăn xếp CALL được bật lên và thực thi lại từ vị trí tệp đã lưu.
Giai đoạn 7 không được thực thi cho các tập lệnh GỌI hoặc: nhãn.
- Khác kết quả của giai đoạn 6 rơi vào giai đoạn 7 để thực hiện.
Giai đoạn 7) Thực thi: Lệnh được thực thi
- 7.1 - Thực thi lệnh nội bộ - Nếu mã thông báo lệnh được trích dẫn, sau đó bỏ qua bước này. Nếu không, cố gắng phân tích một lệnh nội bộ và thực thi.
- Các thử nghiệm sau đây được thực hiện để xác định xem mã thông báo lệnh không trích dẫn có đại diện cho lệnh nội bộ hay không:
- Nếu mã thông báo lệnh khớp chính xác với một lệnh nội bộ, thì thực hiện nó.
- Khác phá vỡ mã thông báo lệnh trước khi xuất hiện lần đầu tiên
+
/
[
]
<space>
<tab>
,
;
hoặc =
Nếu văn bản trước đó là một lệnh nội bộ, thì hãy nhớ lệnh đó
- Nếu ở chế độ dòng lệnh hoặc nếu lệnh từ khối được ngoặc đơn, thì khối lệnh IF đúng hoặc sai, khối lệnh FOR DO hoặc liên quan đến nối lệnh, sau đó thực hiện lệnh bên trong
- Khác (phải là một lệnh độc lập trong chế độ hàng loạt) quét thư mục hiện tại và PATH để tìm tệp .COM, .EXE, .BAT hoặc .CMD có tên cơ sở khớp với mã thông báo lệnh gốc
- Nếu tệp phù hợp đầu tiên là .BAT hoặc .CMD, thì hãy goto 7.3.exec và thực thi tập lệnh đó
- Khác (không tìm thấy kết quả khớp hoặc khớp đầu tiên là .EXE hoặc .COM) thực thi lệnh nội bộ đã nhớ
- Khác phá vỡ mã thông báo lệnh trước lần xuất hiện đầu tiên
.
\
hoặc :
Nếu văn bản trước không phải là lệnh nội bộ, thì goto 7.2
Khác văn bản trước có thể là lệnh nội bộ. Ghi nhớ lệnh này.
- Phá mã thông báo lệnh trước khi xuất hiện lần đầu tiên
+
/
[
]
<space>
<tab>
,
;
hoặc =
Nếu văn bản trước đó là đường dẫn đến tệp hiện có, thì goto 7.2
Else thực thi lệnh nội bộ đã nhớ.
- Nếu một lệnh nội bộ được phân tích cú pháp từ mã thông báo lệnh lớn hơn, thì phần không được sử dụng của mã thông báo lệnh được bao gồm trong danh sách đối số
- Chỉ vì mã thông báo lệnh được phân tích cú pháp như một lệnh nội bộ không có nghĩa là nó sẽ thực thi thành công. Mỗi lệnh nội bộ có các quy tắc riêng về cách các đối số và tùy chọn được phân tích cú pháp và cú pháp nào được cho phép.
- Tất cả các lệnh nội bộ sẽ in trợ giúp thay vì thực hiện chức năng của chúng nếu
/?
được phát hiện. Hầu hết nhận ra /?
nếu nó xuất hiện bất cứ nơi nào trong các đối số. Nhưng một vài lệnh như ECHO và SET chỉ in trợ giúp nếu mã thông báo đối số đầu tiên bắt đầu bằng /?
.
- SET có một số ngữ nghĩa thú vị:
- Nếu lệnh SET có dấu ngoặc kép trước khi tên biến và phần mở rộng được bật
set "name=content" ignored
-> value = content
thì văn bản giữa dấu bằng đầu tiên và trích dẫn cuối cùng được sử dụng làm nội dung (loại trừ trích dẫn đầu tiên và cuối cùng). Văn bản sau khi trích dẫn cuối cùng được bỏ qua. Nếu không có trích dẫn sau dấu bằng, thì phần còn lại của dòng được sử dụng làm nội dung.
- Nếu lệnh SET không có
dấu ngoặc kép trước tên
set name="content" not ignored
-> value = "content" not ignored
thì toàn bộ phần còn lại của dòng sau bằng được sử dụng làm nội dung, bao gồm bất kỳ và tất cả các trích dẫn có thể có mặt.
- Một so sánh IF được đánh giá và tùy thuộc vào điều kiện là đúng hay sai, khối lệnh phụ thuộc đã được phân tích cú pháp thích hợp được xử lý, bắt đầu với giai đoạn 5.
- Mệnh đề IN của lệnh FOR được lặp lại một cách thích hợp.
- Nếu đây là FOR / F lặp lại đầu ra của khối lệnh, thì:
- Mệnh đề IN được thực thi trong một quy trình cmd.exe mới thông qua CMD / C.
- Khối lệnh phải trải qua toàn bộ quá trình phân tích cú pháp lần thứ hai, nhưng lần này trong bối cảnh dòng lệnh
- ECHO sẽ bắt đầu BẬT và việc mở rộng bị trì hoãn thường sẽ bị vô hiệu hóa (phụ thuộc vào cài đặt đăng ký)
- Tất cả các thay đổi môi trường được thực hiện bởi khối lệnh mệnh đề IN sẽ bị mất sau khi quá trình cmd.exe con kết thúc
- Đối với mỗi lần lặp:
- Các giá trị biến FOR được xác định
- Khối lệnh DO đã được phân tích cú pháp sau đó được xử lý, bắt đầu với giai đoạn 4.
- GOTO sử dụng logic sau để định vị nhãn:
- Nhãn được phân tích cú pháp từ mã thông báo đối số đầu tiên
- Kịch bản được quét cho lần xuất hiện tiếp theo của nhãn
- Quá trình quét bắt đầu từ vị trí tệp hiện tại
- Nếu kết thúc tập tin, thì vòng quét trở lại điểm bắt đầu của tập tin và tiếp tục đến điểm bắt đầu ban đầu.
- Quá trình quét dừng lại ở lần xuất hiện đầu tiên của nhãn mà nó tìm thấy và con trỏ tệp được đặt thành dòng ngay sau nhãn. Thi hành kịch bản tiếp tục từ thời điểm đó. Lưu ý rằng một GOTO thực sự thành công sẽ ngay lập tức hủy bỏ bất kỳ khối mã được phân tích cú pháp nào, bao gồm các vòng lặp FOR.
- Nếu không tìm thấy nhãn hoặc mã thông báo nhãn bị thiếu, thì GOTO không thành công, thông báo lỗi được in và ngăn xếp cuộc gọi được bật lên. Điều này hoạt động hiệu quả như một EXIT / B, ngoại trừ mọi lệnh đã được phân tích cú pháp trong khối lệnh hiện tại theo GOTO vẫn được thực thi, nhưng trong ngữ cảnh của CALLer (bối cảnh tồn tại sau EXIT / B)
- Xem https://www.dostips.com/forum/viewtopic.php?f=3&t=3803 để biết mô tả chính xác hơn về các quy tắc được sử dụng để phân tích nhãn.
- RENAME và COPY đều chấp nhận ký tự đại diện cho đường dẫn nguồn và đích. Nhưng Microsoft thực hiện một công việc khủng khiếp là tài liệu về cách các ký tự đại diện hoạt động, đặc biệt là cho đường dẫn đích. Có thể tìm thấy một bộ quy tắc ký tự đại diện hữu ích tại Làm thế nào để lệnh Windows RENAME diễn giải các ký tự đại diện?
- 7.2 - Thực hiện thay đổi âm lượng - Khác nếu mã thông báo lệnh không bắt đầu bằng trích dẫn, dài chính xác hai ký tự và ký tự thứ 2 là dấu hai chấm, sau đó thay đổi âm lượng
- Tất cả các mã thông báo đối số được bỏ qua
- Nếu không thể tìm thấy âm lượng được chỉ định bởi ký tự đầu tiên, thì hủy bỏ với lỗi
- Mã thông báo lệnh
::
sẽ luôn dẫn đến lỗi trừ khi SUBST được sử dụng để xác định âm lượng cho ::
Nếu SUBST được sử dụng để xác định âm lượng ::
, thì âm lượng sẽ bị thay đổi, nó sẽ không được coi là nhãn.
- 7.3 - Thực thi lệnh bên ngoài - Khác cố gắng coi lệnh như một lệnh bên ngoài.
- Nếu trong chế độ dòng lệnh và lệnh không được trích dẫn và không bắt đầu bằng một đặc tả khối lượng, trắng-không gian,
,
, ;
, =
hoặc +
sau đó phá vỡ các lệnh mã thông báo tại sự xuất hiện đầu tiên của <space>
,
;
hay =
và thêm vào trước thời gian còn lại để lập luận token (s).
- Nếu ký tự thứ 2 của mã thông báo lệnh là dấu hai chấm, thì có thể xác minh âm lượng được chỉ định bởi ký tự thứ 1 có thể được tìm thấy.
Nếu âm lượng không thể được tìm thấy, sau đó hủy bỏ với một lỗi.
- Nếu ở chế độ hàng loạt và mã thông báo lệnh bắt đầu bằng
:
, thì goto 7.4
Lưu ý rằng nếu mã thông báo nhãn bắt đầu bằng ::
thì điều này sẽ không đạt được vì bước trước đó sẽ bị hủy bỏ trừ khi sử dụng SUBST để xác định âm lượng ::
.
- Xác định lệnh bên ngoài để thực thi.
- Đây là một quá trình phức tạp có thể liên quan đến khối lượng hiện tại, thư mục hiện tại, biến PATH, biến PATHEXT và các liên kết tệp.
- Nếu một lệnh bên ngoài hợp lệ không thể được xác định, sau đó hủy bỏ với một lỗi.
- Nếu ở chế độ dòng lệnh và mã thông báo lệnh bắt đầu
:
, thì goto 7.4
Lưu ý rằng điều này hiếm khi đạt được vì bước trước đó sẽ bị hủy bỏ với lỗi trừ khi mã thông báo lệnh bắt đầu ::
và SUBST được sử dụng để xác định âm lượng cho ::
và toàn bộ mã thông báo lệnh là một đường dẫn hợp lệ đến một lệnh bên ngoài.
- 7.3.exec - Thực hiện lệnh bên ngoài.
- 7.4 - Bỏ qua nhãn - Bỏ qua lệnh và tất cả các đối số của nó nếu mã thông báo lệnh bắt đầu bằng
:
.
Các quy tắc trong 7.2 và 7.3 có thể ngăn nhãn đạt đến điểm này.
Trình phân tích dòng lệnh:
Hoạt động như BatchLine-Parser, ngoại trừ:
Giai đoạn 1) Mở rộng phần trăm:
- Không
%*
, %1
v.v.
- Nếu var không được xác định, thì
%var%
không thay đổi.
- Không xử lý đặc biệt
%%
. Nếu var = nội dung, sau đó %%var%%
mở rộng thành %content%
.
Giai đoạn 3) Báo lại (các) lệnh được phân tích cú pháp
- Điều này không được thực hiện sau giai đoạn 2. Nó chỉ được thực hiện sau giai đoạn 4 cho khối lệnh FOR DO.
Giai đoạn 5) Mở rộng bị trì hoãn: chỉ khi DelayedExpansion được bật
- Nếu var không được xác định, thì
!var!
không thay đổi.
Giai đoạn 7) Thực thi lệnh
- Nỗ lực GỌI hoặc GOTO a: nhãn dẫn đến lỗi.
- Như đã được ghi lại trong giai đoạn 7, một nhãn thực thi có thể dẫn đến lỗi trong các tình huống khác nhau.
- Nhãn thực thi hàng loạt chỉ có thể gây ra lỗi nếu chúng bắt đầu bằng
::
- Các dòng lệnh thực thi hầu như luôn luôn dẫn đến một lỗi
Phân tích cú pháp các giá trị nguyên
Có nhiều bối cảnh khác nhau trong đó cmd.exe phân tích các giá trị nguyên từ các chuỗi và các quy tắc không nhất quán:
SET /A
IF
%var:~n,m%
(mở rộng chuỗi con thay đổi)
FOR /F "TOKENS=n"
FOR /F "SKIP=n"
FOR /L %%A in (n1 n2 n3)
EXIT [/B] n
Chi tiết cho các quy tắc này có thể được tìm thấy tại Quy tắc về cách CMD.EXE phân tích số
Đối với bất kỳ ai muốn cải thiện các quy tắc phân tích cú pháp cmd.exe, có một chủ đề thảo luận trên diễn đàn DosTips nơi các vấn đề có thể được báo cáo và đề xuất.
Hy vọng nó sẽ giúp
Jan Erik (jeb) - Tác giả và người phát hiện ra các giai đoạn
Dave Benham (dbenham) - Nhiều nội dung và chỉnh sửa bổ sung