Bagaimana saya bisa mengetahui secara program apakah nama file cocok dengan pola glob shell?


13

Saya ingin memberi tahu apakah string $stringakan dicocokkan dengan pola gumpalan $pattern. $stringmungkin atau mungkin bukan nama file yang ada. Bagaimana saya bisa melakukan ini?

Asumsikan format berikut untuk string input saya:

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

Saya ingin menemukan idiom bash yang menentukan apakah $stringakan dicocokkan oleh $pattern1, $pattern2atau pola segumpal sewenang-wenang lainnya. Inilah yang saya coba sejauh ini:

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

    Ini hampir berhasil, kecuali yang $patternditafsirkan sebagai pola string dan bukan sebagai pola glob.

  2. [ "$string" = $pattern ]

    Masalah dengan pendekatan ini adalah yang $patterndiperluas dan kemudian perbandingan string dilakukan antara $stringdan perluasan $pattern.

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

    Yang ini berfungsi, tetapi hanya jika $stringberisi file yang ada.

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

    Ini tidak berfungsi karena =~operator menyebabkan $patternditafsirkan sebagai ekspresi reguler yang diperluas, bukan pola gumpalan atau wildcard.


2
Masalah yang akan Anda hadapi adalah bahwa {bar,baz}itu bukan pola. Ini ekspansi parameter. Perbedaan yang halus namun kritis dalam hal {bar,baz}itu diperluas sejak awal menjadi banyak argumen, bardan baz.
Patrick

Jika shell dapat memperluas parameter, maka pastinya ia dapat mengetahui apakah sebuah string merupakan ekspansi potensial dari sebuah bola.
jayhendren

coba ini a = ls /foo/* sekarang kamu bisa mencocokkannya dalam
Hackaholic

1
@ Patrick: setelah membaca halaman bash man, saya telah belajar bahwa foo/{bar,baz}sebenarnya adalah ekspansi brace (bukan ekspansi parameter) sementara foo/*ekspansi pathname. $stringadalah ekspansi parameter. Ini semua dilakukan pada waktu yang berbeda dan dengan mekanisme yang berbeda.
jayhendren

@ jayhendren @ Patrick adalah benar, dan kemudian Anda mengetahui bahwa pertanyaan Anda pada akhirnya bukanlah apa yang membuat orang percaya. Sebaliknya, Anda ingin mencocokkan string dengan berbagai jenis pola. Jika Anda ingin benar-benar cocok dengan pola gumpalan, casepernyataan melakukan Pathname Expansion ("globbing") sesuai dengan manual Bash.
Mike S

Jawaban:


8

Tidak ada solusi umum untuk masalah ini. Alasannya adalah bahwa, dalam bash, ekspansi brace (yaitu, {pattern1,pattern2,...}dan ekspansi nama file (alias pola glob) dianggap sebagai hal yang terpisah dan diperluas dalam kondisi yang berbeda dan pada waktu yang berbeda. Berikut adalah daftar lengkap ekspansi yang dilakukan bash:

  • ekspansi brace
  • ekspansi tilde
  • parameter dan ekspansi variabel
  • substitusi komando
  • ekspansi aritmatika
  • pemisahan kata
  • ekspansi pathname

Karena kami hanya peduli pada subset dari ini (mungkin brace, tilde, dan ekspansi pathname), dimungkinkan untuk menggunakan pola dan mekanisme tertentu untuk membatasi ekspansi dengan cara yang terkendali. Contohnya:

#!/bin/bash
set -f

string=/foo/bar

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

Menjalankan skrip ini menghasilkan output berikut:

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

Ini berfungsi karena set -fmenonaktifkan ekspansi pathname, jadi hanya ekspansi brace dan ekspansi tilde terjadi dalam pernyataan for pattern in /foo/{*,foo*,bar*,**,**/*}. Kita kemudian dapat menggunakan operasi tes [[ $string == $pattern ]]untuk menguji terhadap ekspansi pathname setelah ekspansi brace telah dilakukan.


7

Saya tidak percaya itu {bar,baz} adalah pola glob shell (meskipun tentu saja /foo/ba[rz]) tetapi jika Anda ingin tahu apakah $stringcocok $patternAnda bisa melakukannya:

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

Anda dapat melakukan sebanyak yang Anda suka:

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

1
Hai @ mikeserv, seperti ditunjukkan dalam komentar dan jawaban yang saya berikan di atas, saya sudah belajar bahwa apa yang Anda katakan itu benar - {bar,baz}bukan pola gumpalan. Saya sudah menemukan solusi untuk pertanyaan saya yang memperhitungkan hal ini.
jayhendren

3
+1 Ini menjawab pertanyaan persis seperti yang diberikan dalam judul, dan kalimat pertama. meskipun pertanyaannya kemudian mengonfigurasi pola lain dengan pola glob shell.
Mike S

3

Seperti yang ditunjukkan Patrick, Anda membutuhkan "jenis pola" yang berbeda:

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


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

Kutipan tidak diperlukan di sana.


Oke, ini berhasil, tapi sebenarnya tidak menjawab pertanyaan saya. Misalnya, saya ingin mempertimbangkan pola-pola yang berasal dari sumber lain, yaitu pola-pola itu di luar kendali saya.
jayhendren

@ jayhendren Maka Anda mungkin harus terlebih dahulu mengonversi pola masuk ke bash accepts.
Hauke ​​Laging

Jadi semua yang telah Anda lakukan adalah mengubah pertanyaan saya dari "bagaimana saya tahu jika nama file adalah potensi perluasan ekspresi" menjadi "bagaimana cara mengubah pola nama file bash-style normal menjadi bash-style extended glob pattern."
jayhendren

@ jayhendren Menimbang bahwa apa yang Anda inginkan tampaknya tidak mungkin "semua yang telah Anda lakukan" terdengar agak aneh bagi saya, tetapi mungkin itu hanya masalah bahasa asing. Jika Anda ingin mengajukan pertanyaan baru ini maka Anda harus menjelaskan seperti apa pola input. Mungkin ini sedoperasi sederhana .
Hauke ​​Laging

1
@ HaukeLaging benar. Orang-orang yang datang ke sini untuk mencari tahu bagaimana cara mencocokkan dengan pola glob (alias "Ekspansi Pathname") cenderung bingung, seperti yang saya lakukan, karena judulnya mengatakan "pola glob shell" tetapi isi pertanyaannya menggunakan pola non-glob . Pada titik tertentu, jayhendren belajar tentang perbedaan itu tetapi kebingungan awalnya adalah yang menyebabkan Hauke ​​menjawab seperti yang dia lakukan.
Mike S

1

Saya akan menggunakan grep dengan demikian:

#!/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

Yang memberikan output:

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

-2

dalam zsh:

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

1
Pertanyaannya menyatakan bahwa bash shell akan digunakan.
jayhendren
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.