Làm cách nào để tôi có thể biết được tên tệp có khớp với mẫu toàn cầu không?


13

Tôi muốn biết nếu một chuỗi $stringsẽ được khớp bởi một mẫu hình cầu $pattern. $stringcó thể hoặc không thể là tên của một tập tin hiện có. Tôi có thể làm cái này như thế nào?

Giả sử các định dạng sau cho chuỗi đầu vào của tôi:

string="/foo/bar"
pattern1="/foo/*"
pattern2="/foo/{bar,baz}"

Tôi muốn tìm một thành ngữ bash xác định nếu $stringcó thể được kết hợp bởi $pattern1, $pattern2hoặc bất kỳ mẫu glob tùy ý khác. Đây là những gì tôi đã cố gắng cho đến nay:

  1. [[ "$string" = $pattern ]]

    Điều này gần như hoạt động, ngoại trừ $patternđược hiểu là một mẫu chuỗi và không phải là một mẫu toàn cầu.

  2. [ "$string" = $pattern ]

    Vấn đề với cách tiếp cận này là $patternđược mở rộng và sau đó so sánh chuỗi được thực hiện giữa $stringvà mở rộng $pattern.

  3. [[ "$(find $pattern -print0 -maxdepth 0 2>/dev/null)" =~ "$string" ]]

    Cái này hoạt động, nhưng chỉ khi $stringchứa một tập tin tồn tại.

  4. [[ $string =~ $pattern ]]

    Điều này không hoạt động vì =~toán tử gây ra $patternđược hiểu là biểu thức chính quy mở rộng, không phải là mẫu hình cầu hoặc ký tự đại diện.


2
Vấn đề bạn sẽ gặp phải đó {bar,baz}không phải là một mô hình. Đó là mở rộng tham số. Sự khác biệt tinh tế nhưng quan trọng trong đó {bar,baz}được mở rộng từ rất sớm thành nhiều đối số, barbaz.
Patrick

Nếu shell có thể mở rộng các tham số, thì chắc chắn nó có thể biết liệu một chuỗi có phải là sự mở rộng tiềm năng của một quả địa cầu hay không.
jayhendren

hãy thử xem a = ls /foo/* bây giờ bạn có thể khớp trong một
Hackaholic

1
@Patrick: sau khi đọc qua trang bash man, tôi đã biết rằng đó foo/{bar,baz}thực sự là một mở rộng cú đúp (không phải là mở rộng tham số) trong khi foo/*là mở rộng tên đường dẫn. $stringlà mở rộng tham số. Tất cả đều được thực hiện tại các thời điểm khác nhau và theo các cơ chế khác nhau.
jayhendren

@jayhendren @Patrick là đúng, và sau đó bạn đã học được rằng câu hỏi của bạn cuối cùng không phải là những gì tiêu đề dẫn người ta tin. Thay vào đó, bạn muốn khớp một chuỗi với các loại mẫu khác nhau. Nếu bạn muốn khớp chính xác so với mẫu toàn cục, casecâu lệnh sẽ thực hiện Mở rộng tên đường dẫn ("globalbing") theo hướng dẫn Bash.
Mike S

Câu trả lời:


8

Không có giải pháp chung cho vấn đề này. Lý do là, trong bash, mở rộng dấu ngoặc (nghĩa là {pattern1,pattern2,...}mở rộng tên tệp (còn gọi là mẫu hình cầu) được coi là những thứ riêng biệt và được mở rộng trong các điều kiện khác nhau và tại các thời điểm khác nhau. Dưới đây là danh sách đầy đủ các mở rộng mà bash thực hiện:

  • mở rộng cú đúp
  • mở rộng dấu ngã
  • mở rộng tham số và biến
  • thay thế lệnh
  • mở rộng số học
  • chia từ
  • mở rộng tên đường dẫn

Vì chúng tôi chỉ quan tâm đến một tập hợp con trong số này (có thể là niềng răng, dấu ngã và mở rộng tên đường dẫn), nên có thể sử dụng các mẫu và cơ chế nhất định để hạn chế mở rộng theo kiểu có thể kiểm soát được. Ví dụ:

#!/bin/bash
set -f

string=/foo/bar

for pattern in /foo/{*,foo*,bar*,**,**/*}; do
    [[ $string == $pattern ]] && echo "$pattern matches $string"
done

Chạy tập lệnh này tạo ra đầu ra sau:

/foo/* matches /foo/bar
/foo/bar* matches /foo/bar
/foo/** matches /foo/bar

Điều này hoạt động vì set -fvô hiệu hóa mở rộng tên đường dẫn, vì vậy chỉ có mở rộng dấu ngoặc và mở rộng dấu ngã xảy ra trong câu lệnh for pattern in /foo/{*,foo*,bar*,**,**/*}. Sau đó chúng ta có thể sử dụng thao tác kiểm tra [[ $string == $pattern ]]để kiểm tra chống lại việc mở rộng tên đường dẫn sau khi mở rộng dấu ngoặc đã được thực hiện.


7

Tôi không tin rằng đó {bar,baz} mô hình toàn cầu vỏ (mặc dù chắc chắn /foo/ba[rz]là vậy) nhưng nếu bạn muốn biết liệu có $stringphù hợp hay $patternkhông:

case "$string" in 
($pattern) put your successful execution statement here;;
(*)        this is where your failure case should be   ;;
esac

Bạn có thể làm bao nhiêu tùy thích:

case "$string" in
($pattern1) do something;;
($pattern2) do differently;;
(*)         still no match;;
esac

1
Xin chào @mikeerv, như đã nêu trong các nhận xét và câu trả lời mà tôi đã cung cấp ở trên, tôi đã biết rằng những gì bạn nói là đúng - {bar,baz}không phải là mô hình toàn cầu. Tôi đã đưa ra một giải pháp cho câu hỏi của mình có tính đến điều này.
jayhendren

3
+1 Điều này trả lời chính xác câu hỏi như trong tiêu đề và câu đầu tiên. mặc dù câu hỏi sau đó kết hợp các mẫu khác với các mẫu toàn cầu.
Mike S

3

Như Patrick đã chỉ ra rằng bạn cần một "kiểu khác" của mẫu:

[[ /foo/bar == /foo/@(bar|baz) ]]


string="/foo/bar"
pattern="/foo/@(bar|baz)"
[[ $string == $pattern ]]

Báo giá là không cần thiết ở đó.


Ok, điều này hoạt động, nhưng nói đúng ra, nó không trả lời câu hỏi của tôi. Chẳng hạn, tôi muốn xem xét các mẫu đến từ một nguồn khác, tức là các mẫu nằm ngoài tầm kiểm soát của tôi.
jayhendren

@jayhendren Sau đó, trước tiên bạn có thể phải chuyển đổi mô hình đến thành những chấp nhận bash đó.
Hauke ​​Laging

Vì vậy, tất cả những gì bạn thực sự đã chuyển đổi câu hỏi của tôi từ "làm thế nào để biết tên tệp có phải là sự mở rộng tiềm năng của một biểu thức" thành "làm cách nào để chuyển đổi các mẫu tên tệp kiểu bash bình thường thành các mẫu hình cầu mở rộng theo kiểu bash."
jayhendren

@jayhendren Xem xét rằng những gì bạn muốn dường như là không thể "tất cả những gì bạn thực sự làm" nghe có vẻ hơi lạ đối với tôi nhưng có lẽ đó chỉ là một thứ ngoại ngữ. Nếu bạn muốn hỏi câu hỏi mới này thì bạn phải giải thích các mẫu đầu vào trông như thế nào. Có lẽ đó là một sedhoạt động đơn giản .
Hauke ​​Laging

1
@HaukeLaging là chính xác. Mọi người đến đây để tìm hiểu làm thế nào để khớp với các mẫu toàn cầu (còn gọi là "Mở rộng tên đường") có thể bị nhầm lẫn, như tôi đã làm, bởi vì tiêu đề nói "mẫu toàn cầu vỏ" nhưng nội dung câu hỏi của anh ta sử dụng mẫu không phải toàn cầu . Tại một số thời điểm, jayhendren đã biết về sự khác biệt nhưng sự nhầm lẫn ban đầu của anh ta là nguyên nhân khiến Hauke ​​trả lời theo cách anh ta đã làm.
Mike S

1

Tôi sẽ sử dụng grep như vậy:

#!/bin/bash
string="/foo/bar"
pattern1="/foo/*"
pattern2="/foo/{bar,baz}"

if echo $string | grep -e "$pattern1" > /dev/null; then
    echo $string matches $pattern1
fi

if echo $string | grep -e "$pattern2" > /dev/null; then
    echo $string matches $pattern2
fi

Cung cấp đầu ra:

./test2.bsh 
/foo/bar matches /foo/*

-2

trong zsh:

[[ $string = $~pattern ]] && print true

1
Câu hỏi nói rằng vỏ bash sẽ được sử dụng.
jayhendren
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.