Kiểm tra xem một chuỗi có khớp với biểu thức chính quy trong tập lệnh Bash không


204

Một trong những đối số mà tập lệnh của tôi nhận được là một ngày ở định dạng sau : yyyymmdd.

Tôi muốn kiểm tra xem tôi có nhận được một ngày hợp lệ làm đầu vào không.

Tôi có thể làm cái này như thế nào? Tôi đang cố gắng sử dụng regex như:[0-9]\{\8}


Kiểm tra nếu định dạng đúng là dễ dàng. Nhưng tôi không nghĩ rằng bạn có thể, trong bash (có tích hợp sẵn), kiểm tra xem ngày có hợp lệ không.
RedX

Câu trả lời:


316

Bạn có thể sử dụng cấu trúc kiểm tra [[ ]], cùng với toán tử khớp biểu thức chính quy =~, để kiểm tra xem một chuỗi có khớp với mẫu biểu thức chính quy không.

Đối với trường hợp cụ thể của bạn, bạn có thể viết:

[[ $date =~ ^[0-9]{8}$ ]] && echo "yes"

Hoặc nhiều hơn một bài kiểm tra chính xác:

[[ $date =~ ^[0-9]{4}(0[1-9]|1[0-2])(0[1-9]|[1-2][0-9]|3[0-1])$ ]] && echo "yes"
#           |^^^^^^^^ ^^^^^^ ^^^^^^  ^^^^^^ ^^^^^^^^^^ ^^^^^^ |
#           |   |     ^^^^^^^^^^^^^  ^^^^^^^^^^^^^^^^^^^^^^^^ |
#           |   |          |                   |              |
#           |   |           \                  |              |
#           | --year--   --month--           --day--          |
#           |          either 01...09      either 01..09     end of line
# start of line            or 10,11,12         or 10..29
#                                              or 30, 31

Đó là, bạn có thể định nghĩa một biểu thức chính quy trong Bash khớp với định dạng bạn muốn. Bằng cách này bạn có thể làm:

[[ $date =~ ^regex$ ]] && echo "matched" || echo "did not match"

trong đó các lệnh sau &&được thực thi nếu thử nghiệm thành công và các lệnh sau ||được thực hiện nếu thử nghiệm không thành công.

Lưu ý điều này dựa trên giải pháp của Aleks-Daniel Jakimenko trong xác minh định dạng ngày nhập của người dùng trong bash .


Trong các shell khác, bạn có thể sử dụng grep . Nếu vỏ của bạn tuân thủ POSIX, hãy làm

(echo "$date" | grep -Eq  ^regex$) && echo "matched" || echo "did not match"

, không tuân thủ POSIX, bạn có thể làm

echo "$date" | grep -Eq "^regex\$"; and echo "matched"; or echo "did not match"

19
Tôi nhận thức được điều đó, nhưng tôi cũng thích tính đến người đang hỏi và họ đi bao xa với bash. Nếu chúng tôi cung cấp các điều kiện rất phức tạp, họ sẽ không học được gì và chỉ quay lại bất cứ khi nào họ có nghi ngờ khác. Tôi thích đưa ra một câu trả lời dễ hiểu hơn.
fedorqui 'SO ngừng làm hại'

7
Heh. Chà, cách duy nhất để học là đọc rất nhiều mã tốt. Nếu bạn cung cấp mã sai dễ hiểu nhưng không được khuyến khích sử dụng - đó là một cách dạy không hay. Ngoài ra, tôi khá chắc chắn rằng với những người mới bắt đầu học bash (có thể đã biết một số bit của ngôn ngữ khác) sẽ hiểu cú pháp bash cho regex dễ dàng hơn so với một số greplệnh có -Ecờ.
Aleks-Daniel Jakimenko-A.

8
@ Aleks-DanielJakimenko Tôi đã đi qua bài đăng này một lần nữa và bây giờ tôi đồng ý tốt nhất là sử dụng bash regex. Cảm ơn đã chỉ theo hướng tốt, cập nhật câu trả lời.
fedorqui 'SO ngừng làm hại'

4
Upvote, cho phép sử dụng nó một chút xa hơn so với câu hỏi OP, cho sh, ví dụ ..
Dereckson

3
@ Aleks-DanielJakimenko sử dụng grep có vẻ là lựa chọn tốt nhất nếu bạn đang sử dụng sh, fishhoặc vỏ ít trang bị khác.
tomekwi

47

Trong bash phiên bản 3, bạn có thể sử dụng toán tử '= ~':

if [[ "$date" =~ ^[0-9]{8}$ ]]; then
    echo "Valid date"
else
    echo "Invalid date"
fi

Tham khảo: http://tldp.org/LDP/abs/html/bashver3.html#REGEXMATCHREF

LƯU Ý: Việc trích dẫn trong toán tử khớp trong dấu ngoặc kép, [[]], không còn cần thiết kể từ phiên bản Bash 3.2


20
Bạn không nên sử dụng char "trong expresion thường xuyên? Bởi vì khi tôi sử dụng expresion không hoạt động
Dawid Drozd

Hơn nữa, dấu gạch chéo ngược của {và} cũng có vấn đề tương tự.
kbulgrien

32

Một cách tốt để kiểm tra xem một chuỗi có đúng ngày hay không là sử dụng ngày lệnh:

if date -d "${DATE}" >/dev/null 2>&1
then
  # do what you need to do with your date
else
  echo "${DATE} incorrect date" >&2
  exit 1
fi

từ bình luận: người ta có thể sử dụng định dạng

if [ "2017-01-14" == $(date -d "2017-01-14" '+%Y-%m-%d') ] 

9
Đánh giá cao câu trả lời của bạn vì nó cho phép chức năng ngày xử lý ngày và không phải là biểu thức dễ bị lỗi '
Ali

Điều này tốt cho việc kiểm tra các tùy chọn ngày rộng, nhưng nếu bạn cần xác minh một định dạng ngày cụ thể, nó có thể làm điều đó không? Ví dụ: nếu tôi làm điều date -d 2017-11-14eđó sẽ trả về Thứ ba ngày 14 tháng 11 05:00:00 UTC 2017, nhưng điều đó sẽ phá vỡ kịch bản của tôi.
Giô

1
Bạn có thể sử dụng một cái gì đó tương tự: if ["2017-01-14" == $ (ngày -d "2017-01-14" '+% Y-% m-% d')] Nó kiểm tra xem ngày có đúng không và kiểm tra xem kết quả có giống với dữ liệu đã nhập của bạn không. Nhân tiện, hãy cẩn thận với định dạng ngày được bản địa hóa (ví dụ: Tháng-Năm so với Ngày-Tháng-Năm)
Django Janny

1
Có thể không hoạt động, tùy thuộc vào địa phương của bạn. Ngày định dạng của người Mỹ sử dụng MM-DD-YYYY sẽ không hoạt động ở bất kỳ nơi nào khác trên thế giới, sử dụng DD-MM-YYYY (Châu Âu) hoặc YYYY-MM-DD (một số nơi ở Châu Á)
Paul

@Paul, cái gì có thể không hoạt động? Như được viết trong một bình luận, người ta có thể sử dụng các tùy chọn định dạng ...
Betlista

4

Tôi sẽ sử dụng expr matchthay vì =~:

expr match "$date" "[0-9]\{8\}" >/dev/null && echo yes

Điều này tốt hơn câu trả lời hiện đang được chấp nhận sử dụng =~=~cũng sẽ khớp với các chuỗi trống, mà IMHO không nên sử dụng. Giả sử badvarkhông được xác định, sau đó [[ "1234" =~ "$badvar" ]]; echo $?đưa ra (không chính xác) 0, trong khi expr match "1234" "$badvar" >/dev/null ; echo $?cho kết quả chính xác 1.

Chúng ta phải sử dụng >/dev/nulldễ dàng ngụy trang expr matchcủa giá trị sản lượng , mà là số ký tự tương ứng hoặc 0 nếu không phù hợp được tìm thấy. Lưu ý giá trị đầu ra của nó khác với trạng thái thoát của nó . Trạng thái thoát là 0 nếu tìm thấy kết quả khớp hoặc 1 khác.

Nói chung, cú pháp cho exprlà:

expr match "$string" "$lead"

Hoặc là:

expr "$string" : "$lead"

nơi $leadlà một biểu thức chính quy. Nó exit statussẽ đúng (0) nếu leadkhớp với phần đầu của string(Có tên nào cho cái này không?). Ví dụ expr match "abcdefghi" "abc"thoát true, nhưng expr match "abcdefghi" "bcd"thoát false. (Tín dụng cho @Carlo Wood để chỉ ra điều này.


7
=~không khớp chuỗi trống, bạn khớp chuỗi với mẫu trống trong ví dụ bạn đưa ra. Cú pháp là string =~ patternvà một mẫu trống phù hợp với mọi thứ.
bstpierre

2
Điều này không khớp với một chuỗi con, nó trả về (để xuất chuẩn) số lượng ký tự đầu phù hợp và trạng thái thoát là đúng nếu có ít nhất 1 ký tự được khớp. Đây là lý do tại sao một chuỗi trống (khớp với 0 ký tự) có trạng thái thoát là sai. Ví dụ expr match "abcdefghi" "^" && echo Matched || echo No match- và expr match "abcdefghi" "bcd" && echo Matched || echo No match- cả hai trả lại "0\nNo match". Khi phù hợp "a.*f"sẽ trở lại "6\nMatched". Do đó, việc sử dụng '^' trong ví dụ của bạn là không cần thiết và đã được ngụ ý.
Carlo Wood

@bstpierre: vấn đề ở đây không phải là liệu người ta có thể hợp lý hóa hành vi =~khớp chuỗi rỗng hay không. Đó là hành vi này có thể là bất ngờ và có thể gây ra lỗi. Tôi đã viết câu trả lời này đặc biệt bởi vì tôi đã bị nó đốt cháy.
Penghe Geng

@PengheGeng Hành vi bất ngờ? Nếu một mẫu không có định nghĩa hoặc ràng buộc, thì thực tế nó không khớp với bất cứ thứ gì. Sự vắng mặt của một mô hình là một trận đấu cho tất cả mọi thứ. Viết mã mạnh mẽ là câu trả lời, không biện minh cho một lời giải thích kém.
Anthony Rutledge

@AnthonyRutledge "mã mạnh" yêu cầu sử dụng tốt nhất các công cụ có sẵn để ngăn ngừa lỗi mã hóa ngẫu nhiên. Trong mã Shell, nơi một biến trống có thể dễ dàng và vô tình được giới thiệu bất cứ lúc nào bằng cách viết sai chính tả, tôi không nghĩ việc cho phép các biến rỗng được khớp là một tính năng mạnh mẽ. Rõ ràng tác giả của GNU exprđồng ý với tôi.
Penghe Geng

0

Trường hợp việc sử dụng regex có thể hữu ích để xác định xem chuỗi ký tự của một ngày có chính xác hay không, nó không thể được sử dụng dễ dàng để xác định xem ngày đó có hợp lệ hay không. Các ví dụ sau sẽ vượt qua biểu thức chính quy, nhưng tất cả đều là ngày không hợp lệ: 20180231, 20190229, 20190431

Vì vậy, nếu bạn muốn xác thực nếu chuỗi ngày của bạn (hãy gọi nó datestr) ở định dạng chính xác, tốt nhất là phân tích chuỗi đó datevà yêu cầu datechuyển đổi chuỗi thành định dạng chính xác. Nếu cả hai chuỗi giống hệt nhau, bạn có định dạng hợp lệ và ngày hợp lệ.

if [[ "$datestr" == $(date -d "$datestr" "+%Y%m%d" 2>/dev/null) ]]; then
     echo "Valid date"
else
     echo "Invalid date"
fi
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.