Có một số điều quan trọng cần biết về [[ ]]
cấu trúc của bash . Đầu tiên:
Việc tách từ và mở rộng tên đường dẫn không được thực hiện trên các từ giữa [[
và ]]
; mở rộng dấu ngã, mở rộng tham số và biến, mở rộng số học, thay thế lệnh, thay thế quy trình và xóa dấu ngoặc kép được thực hiện.
Điều thứ hai:
Một toán tử nhị phân bổ sung, '= ~', có sẵn, ... chuỗi ở bên phải của toán tử được coi là một biểu thức chính quy mở rộng và được so khớp tương ứng ... Bất kỳ phần nào của mẫu có thể được trích dẫn để buộc nó phải khớp như một chuỗi .
Do đó, $v
ở một trong hai bên của =~
sẽ được mở rộng thành giá trị của biến đó, nhưng kết quả sẽ không được mở rộng từ hoặc tên đường dẫn. Nói cách khác, hoàn toàn an toàn nếu để các mở rộng biến không được trích dẫn ở phía bên trái, nhưng bạn cần biết rằng các mở rộng có thể thay đổi sẽ xảy ra ở phía bên phải.
Vì vậy, nếu bạn viết: [[ $x =~ [$0-9a-zA-Z] ]]
, các $0
bên trong regex ở bên phải sẽ được mở rộng trước khi regex được giải thích, mà có lẽ sẽ làm cho regex để thất bại trong việc biên dịch (trừ khi việc mở rộng $0
đầu với một biểu tượng chữ số hoặc dấu chấm câu có giá trị ascii là ít hơn một chữ số). Nếu bạn trích dẫn phía bên phải tương tự như vậy [[ $x =~ "[$0-9a-zA-Z]" ]]
, thì phía bên phải sẽ được coi là một chuỗi thông thường, không phải là một regex (và $0
vẫn sẽ được mở rộng). Điều bạn thực sự muốn trong trường hợp này là[[ $x =~ [\$0-9a-zA-Z] ]]
Tương tự, biểu thức giữa [[
và ]]
được tách thành các từ trước khi regex được diễn giải. Vì vậy, khoảng trắng trong regex cần phải được thoát hoặc được trích dẫn. Nếu bạn muốn để phù hợp với chữ cái, chữ số hoặc các không gian bạn có thể sử dụng: [[ $x =~ [0-9a-zA-Z\ ] ]]
. Các ký tự khác tương tự cần phải được thoát ra, chẳng hạn như #
, sẽ bắt đầu nhận xét nếu không được trích dẫn. Tất nhiên, bạn có thể đặt mẫu vào một biến:
pat="[0-9a-zA-Z ]"
if [[ $x =~ $pat ]]; then ...
Đối với các regex chứa nhiều ký tự cần phải được thoát hoặc trích dẫn để chuyển qua bash's lexer, nhiều người thích kiểu này hơn. Nhưng hãy cẩn thận: Trong trường hợp này, bạn không thể trích dẫn mở rộng biến:
if [[ $x =~ "$pat" ]]; then ...
Cuối cùng, tôi nghĩ những gì bạn đang cố gắng làm là xác minh rằng biến chỉ chứa các ký tự hợp lệ. Cách dễ nhất để thực hiện kiểm tra này là đảm bảo rằng nó không chứa ký tự không hợp lệ. Nói cách khác, một biểu thức như thế này:
valid='0-9a-zA-Z $%&#'
if [[ ! $x =~ [^$valid] ]]; then ...
!
phủ định phép kiểm tra, biến nó thành một toán tử "không khớp" và một [^...]
lớp ký tự regex có nghĩa là "bất kỳ ký tự nào khác ngoài ...
".
Sự kết hợp giữa mở rộng tham số và toán tử regex có thể làm cho cú pháp biểu thức chính quy bash "gần như có thể đọc được", nhưng vẫn có một số lỗi sai. (Không phải lúc nào cũng có?) Một là bạn không thể đưa ]
vào $valid
, ngay cả khi $valid
đã được trích dẫn, ngoại trừ ngay từ đầu. (Đó là quy tắc Posix regex: nếu bạn muốn đưa ]
vào một lớp ký tự, nó cần phải bắt đầu từ đầu. -
Có thể đi ở đầu hoặc cuối, vì vậy nếu bạn cần cả hai ]
và -
, bạn cần bắt đầu bằng ]
và kết thúc bằng -
, dẫn đến biểu tượng cảm xúc regex "Tôi biết tôi đang làm gì" [][-]
:)