Як я можу програмно визначити, чи відповідає ім'я файлу глобальним шаблоном оболонки?


13

Я хотів би сказати, чи буде рядок $stringузгоджений з глобальним шаблоном $pattern. $stringможе бути, а може не бути іменем існуючого файлу. Як я можу це зробити?

Прийміть наступні формати для моїх вхідних рядків:

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

Я хотів би знайти Баш ідіому , який визначає , якщо $stringбуде супроводжуватися $pattern1, $pattern2або будь-яким іншим довільним шаблоном Глоб. Ось що я спробував поки що:

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

    Це майже працює, за винятком того, що $patternінтерпретується як рядковий візерунок, а не як глобальний шаблон.

  2. [ "$string" = $pattern ]

    Проблема такого підходу полягає в тому, що $patternрозгортається, а потім проводиться порівняння рядків між $stringі розширенням $pattern.

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

    Цей працює, але тільки якщо $stringмістить файл, який існує.

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

    Це не працює, тому що =~оператор спричиняє $patternінтерпретацію як розширений регулярний вираз, а не глобул або шаблону підстановки.


2
Проблема, з якою ви збираєтеся зіткнутися, полягає в тому, що {bar,baz}це не зразок. Це розширення параметрів. Тонка, але критична різниця в цьому {bar,baz}розгортається дуже рано на кілька аргументів, barі baz.
Патрік

Якщо оболонка може розширювати параметри, то, безумовно, вона може визначити, чи є рядок потенційним розширенням глобуса.
jayhendren

спробуйте це a = ls /foo/* зараз ви можете
пограти

1
@Patrick: прочитавши сторінку bash man, я дізнався, що foo/{bar,baz}насправді є розширенням дужок (а не розширенням параметрів), а foo/*є розширенням імені шляху. $string- це розширення параметрів. Все це робиться в різний час і за різними механізмами.
jayhendren

@jayhendren @Patrick має рацію, і тоді ви дізналися, що ваше питання в кінцевому рахунку - це не те, що заголовок спонукає вірити. Швидше, ви хочете зіставити рядок проти різних видів шаблонів. Якщо ви хотіли суворо збігатися з глобальним шаблоном, caseоператор виконує розширення імені Pathname ("глобалізація") відповідно до посібника Bash.
Майк S

Відповіді:


8

Загального рішення цієї проблеми немає. Причина полягає в тому, що в bash розширення дужок (тобто {pattern1,pattern2,...}розширення імен файлів (ака-шаблони шаблонів) вважаються окремими речами і розширюються за різних умов і в різний час. Ось повний список розширень, які виконує bash:

  • розширення дужки
  • розширення тильди
  • Розширення параметрів і змінних
  • підміна команд
  • арифметичне розширення
  • розщеплення слів
  • розширення імені шляху

Оскільки ми дбаємо лише про підмножину з них (можливо, розширення дужок, тильди та імені шляху), можливо використовувати певні шаблони та механізми для обмеження розширення керованим способом. Наприклад:

#!/bin/bash
set -f

string=/foo/bar

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

Запуск цього сценарію генерує такий результат:

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

Це працює, тому що set -fвимикає розширення імені шляху, тому в операторі відбувається лише розширення дужок і розширення tilde for pattern in /foo/{*,foo*,bar*,**,**/*}. Потім ми можемо використовувати тестову операцію [[ $string == $pattern ]]для перевірки проти розширення імені шляху після розширення дужки.


7

Я не вважаю , що {bar,baz} це шаблон оболонки Глоб (хоча , звичайно , /foo/ba[rz]є) , але якщо ви хочете знати , якщо $stringматчі $patternви можете зробити:

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

Ви можете робити скільки завгодно:

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

1
Привіт @mikeserv, як зазначено в коментарях та відповіді, яку я подав вище, я вже дізнався, що те, що ви говорите, є правдою - {bar,baz}це не глобальна модель. Я вже придумав рішення свого питання, яке враховує це.
jayhendren

3
+1 Це відповідає на питання точно, як зазначено в заголовку та першому реченні. хоча питання пізніше пов'язує інші зразки з шаблонами глобальних оболонок.
Майк S

3

Як зазначив Патрік, вам потрібен "інший тип" візерунка:

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


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

Цитати там не потрібні.


Гаразд, це працює, але строго кажучи, це не відповідає на моє запитання. Наприклад, я хотів би розглянути шаблони, що надходять з іншого джерела, тобто шаблони поза моїм контролем.
jayhendren

@jayhendren Тоді вам, мабуть, доведеться спочатку перетворити вхідний шаблон у ті, що приймає bash.
Hauke ​​Laging

Отже, все, що ви насправді зробили, - це перетворити моє запитання з "як я можу сказати, чи є ім'я файлу потенційним розширенням виразу" в "як я перетворять звичайні шаблони імен файлів у стилі bash в розширені глобальні шаблони в стилі bash".
jayhendren

@jayhendren Враховуючи, що те, що ти хочеш, здається неможливим, "все, що ти насправді робив", звучить для мене дещо дивно, але, можливо, це лише штука іноземної мови. Якщо ви хочете задати це нове запитання, ви повинні пояснити, як виглядають шаблони введення. Можливо, це проста sedоперація.
Hauke ​​Laging

1
@HaukeLaging правильний. Люди, які приїжджають сюди, щоб зрозуміти, як співставити глобальні шаблони (він же "Розширення імені Pathname"), можуть заплутатися, як і я, тому що в заголовку написано "шаблон глобальної оболонки", але вміст його питання використовує шаблон, який не є глобальним. . В якийсь момент джейхендрен дізнався про різницю, але його первісна плутанина - це те, що змусило Хоке відповісти так, як він це зробив.
Майк S

1

Я би використовував grep таким чином:

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

Що дає вихід:

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

-2

в zsh:

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

1
У запитанні зазначено, що буде використана оболонка bash.
jayhendren
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.