Đặt tùy chọn bash trong lệnh ghép


9

Tôi đã thấy rằng việc thiết lập extglobtùy chọn hệ vỏ trong hợp chất hỗn hợp sẽ dẫn đến sự thất bại của các phản ứng tiếp theo. Các tùy chọn shell có bắt buộc phải được đặt bên ngoài các lệnh ghép không? Tôi không thấy một dấu hiệu nào về yêu cầu như vậy trong các trang bash man.

Ví dụ, tập lệnh sau hoạt động tốt (bản in a.0 a.1):

#!/bin/bash
touch a.0   a.1 \
      a.b.0 a.b.1
shopt -s extglob
ls "a."!(b*)

Tuy nhiên, nếu hai dòng cuối cùng được thực thi dưới dạng lệnh ghép, tập lệnh sẽ thất bại với lỗi sau:

syntax error near unexpected token `('
`    ls "a."!(b*)'

Điều này đã được thử nghiệm bằng các phiên bản bash từ 4.2 đến 4.4 và với một loạt các lệnh ghép :

(1) có điều kiện - if

#!/bin/bash
touch a.0   a.1 \
      a.b.0 a.b.1
if true; then
    shopt -s extglob
    ls "a."!(b*)
fi

(2) niềng răng - { }

#!/bin/bash
touch a.0   a.1 \
      a.b.0 a.b.1
{
    shopt -s extglob
    ls "a."!(b*)
}

(3) subshell - ( ):

#!/bin/bash
touch a.0   a.1 \
      a.b.0 a.b.1
(
    shopt -s extglob
    ls "a."!(b*)
)

Trong mọi trường hợp, nếu shoptđược di chuyển bên ngoài lệnh ghép, tập lệnh thành công.

Câu trả lời:


14

Không có phần nào của lệnh ghép thực thi trước khi toàn bộ lệnh được phân tích cú pháp, được ghi lại một cách rõ ràng trong phần Thao tác Shell của hướng dẫn: tất cả mã thông báo và phân tích cú pháp xảy ra trước khi lệnh "thực thi". Kích hoạt extglobthay đổi cú pháp ngôn ngữ bằng cách thêm các toán tử khớp mẫu mới, được nhận dạng trong quá trình mã hóa.

Bởi vì shoptlệnh đã không được thực thi tại thời điểm !(đạt được, nên nó được xem là mở rộng lịch sử đã cố gắng ( !) hoặc toán tử điều khiển (, thay vì !(được xem như một mục duy nhất (và tương tự @(, v.v., ngoại trừ việc không có mở rộng lịch sử ở đó).

Khi phân tích cú pháp đạt đến mã thông báo đó, một trong hai trường hợp thường sẽ là một lỗi, mặc dù !(...)ở đầu một dòng hoặc sau đó timelà một đường ống con bị phủ định. " unexpected token `('" có nghĩa là nó không thể chấp nhận biểu thức subshell ở vị trí đó.

Điều này hơi khó chịu khi bạn muốn extglob chỉ được kích hoạt tạm thời trong một lớp con. Một cách giải quyết khác là xác định hàm sử dụng toàn cầu mà bạn muốn, sau đó vô hiệu hóa lại extglob và sau đó gọi hàm từ subshell với extglob được kích hoạt:

shopt -s extglob
f() { ls "a."!(b*) ; }
shopt -u extglob
(
    shopt -s extglob
    f
)

Nó rất cục mịch.


Hiệu ứng tương tự xảy ra nếu bạn tạo bí danh trong lệnh ghép, bởi vì chúng cũng được xử lý trước khi phân tích cú pháp:

if true
then
    alias foo=echo
    foo bar
fi
foo xyz

sẽ in foo: command not found, và sau đó xyz, vì bí danh được tạo, nhưng không có sẵn cho đến khi ifhoàn thành.


Cảm ơn - Tôi đã không nhận ra rằng các lệnh ghép được phân tích cú pháp như một đơn vị trước khi thực hiện. Các hành vi bây giờ có ý nghĩa.
dùng001

3
@ user001 Tôi sẽ không từ thiện để nói rằng nó có ý nghĩa. Chuyển đổi chế độ của lexer trong khi phân tích cú pháp là không có gì lạ, và các ngôn ngữ khác như Choặc perlcó thể làm điều đó tốt. Lưu ý rằng không phải là một phần của lệnh ghép không đủ, shopt -s extglobcũng phải nằm trên một dòng riêng biệt. Xem thêm các cuộc thảo luận và ví dụ ở đây
mosvy

@mosvy: Tôi có nghĩa là hành vi được quan sát có ý nghĩa khi người ta nhận ra rằng bash phân tích một lệnh ghép là một đơn vị (không phải là các giới hạn của trình phân tích cú pháp là nhất thiết phải hợp lý). Cảm ơn đã liên kết đến các cuộc thảo luận trong bài khác.
dùng001
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.