Как программно определить, соответствует ли имя файла шаблону оболочки?

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интерпретируется как расширенное регулярное выражение, а не как шаблон глобуса или шаблона.

jayhendren
источник
2
Проблема, с которой вы столкнетесь, заключается в том, что {bar,baz}это не шаблон. Это расширение параметров. Тонкое, но критическое различие в этом {bar,baz}очень рано раскрывается в нескольких аргументах, barи baz.
Патрик
Если оболочка может расширять параметры, то, безусловно, она может сказать, является ли строка потенциальным расширением глоба.
Jayhendren
попробуйте это = ls /foo/* теперь вы можете соответствовать в
Hackaholic
1
@Patrick: прочитав man-страницу bash, я узнал, что foo/{bar,baz}на самом деле это расширение скобок (не расширение параметров), а foo/*расширение путей. $stringэто расширение параметра. Все это делается в разное время и разными механизмами.
Jayhendren
@jayhendren @ Патрик прав, и тогда вы узнали, что ваш вопрос, в конечном счете, не в том, что заставляет поверить название. Скорее, вы хотите сопоставить строку с различными видами шаблонов. Если вы хотите строго сопоставить шаблон с caseглобом , оператор выполняет расширение имени («глобализация») в соответствии с руководством по Bash.
Майк С

Ответы:

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отключает расширение имени пути, поэтому в выражении присутствуют только расширение скобки и расширение тильды for pattern in /foo/{*,foo*,bar*,**,**/*}. Затем мы можем использовать тестовую операцию [[ $string == $pattern ]]для проверки на расширение имени пути после того, как расширение скобки уже выполнено.

jayhendren
источник
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
mikeserv
источник
1
Привет @mikeserv, как указано в комментариях и ответе, который я предоставил выше, я уже узнал, что то, что ты говоришь, правда - {bar,baz}это не шаблон. Я уже пришел к решению моего вопроса, который учитывает это.
Джейхендрен
3
+1 Это отвечает на вопрос точно так, как указано в заголовке и первом предложении. хотя позже этот вопрос объединяет другие шаблоны с шаблонами оболочки.
Майк С
3

Как отметил Патрик, вам нужен шаблон «другого типа»:

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


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

Цитаты там не нужны.

Хауке Лагинг
источник
Хорошо, это работает, но, строго говоря, это не отвечает на мой вопрос. Например, я хотел бы рассмотреть шаблоны, которые приходят из другого источника, то есть шаблоны находятся вне моего контроля.
Jayhendren
@jayhendren Тогда вам, вероятно, придется сначала преобразовать входящий шаблон в эти bash-запросы.
Хауке Лагинг
Таким образом, все, что вы действительно сделали, это преобразовали мой вопрос из «как узнать, является ли имя файла потенциальным расширением выражения» в «как преобразовать обычные шаблоны имен файлов в стиле bash в расширенные шаблоны глобов в стиле bash».
Jayhendren
@jayhendren Считать, что то, что ты хочешь, кажется невозможным «все, что ты на самом деле сделал», звучит немного странно для меня, но, возможно, это просто вещь на иностранном языке. Если вы хотите задать этот новый вопрос, вы должны объяснить, как выглядят шаблоны ввода. Может быть, это простая sedоперация.
Хауке Лагинг
1
@HaukeLaging - это правильно. Люди, которые приходят сюда, чтобы выяснить, как сопоставить шаблоны глобуса (так называемое «Расширение пути»), как и я, могут запутаться, потому что в заголовке написано «шаблон глобуса оболочки», но в содержании его вопроса используется шаблон не глобуса. , В какой-то момент Джейхендрен узнал о разнице, но его первоначальное замешательство заставило Хаука ответить так же, как он.
Майк С
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

в зш:

[[ $string = $~pattern ]] && print true
llua
источник
1
В вопросе говорится, что оболочка bash будет использоваться.
Jayhendren