Команда Linux для сопоставления символов подстановки - PullRequest
1 голос
/ 23 декабря 2009

Есть ли какая-нибудь команда bash, чтобы сделать что-то похожее на:

if [[ $string =~ $pattern ]]

но что он работает с простыми подстановочными знаками (?, *), А не со сложными регулярными выражениями ??


Подробнее:

У меня есть файл конфигурации (своего рода .ini-подобный файл), где каждая строка состоит из шаблона подстановочного знака и некоторых других данных.
Для любой заданной входной строки, которую получает мой скрипт, я должен найти первую строку в файле конфигурации, где шаблон подстановочного знака соответствует входной строке, а затем вернуть остальные данные в этой строке.
Это просто. Мне просто нужен способ сопоставления строки с шаблонами подстановочных знаков, а не RegExps, поскольку шаблоны могут содержать точки, скобки, тире и т. Д., И я не хочу, чтобы они интерпретировались как специальные символы.

Ответы [ 3 ]

3 голосов
/ 23 декабря 2009

У трюка [ -z ${string/$pattern} ] есть несколько довольно серьезных проблем: если строка пуста, она будет соответствовать всем возможным шаблонам; если он содержит пробелы, команда test проанализирует его как часть выражения (попробуйте string="x -o 1 -eq 1" для развлечения). Выражения bash [[выражают подстановочный знак в стиле glob, изначально соответствующий оператору ==, поэтому нет необходимости во всех этих сложных (и подверженных проблемам) приемах. Просто используйте:

if [[ $string == $pattern ]]
2 голосов
/ 23 декабря 2009

Есть несколько способов сделать это.

В bash> = 3 у вас есть соответствие регулярному выражению, как вы описали, например,

$ foo=foobar
$ if [[ $foo =~ f.ob.r ]]; then echo "ok"; fi
   ok

Обратите внимание, что этот синтаксис использует шаблоны регулярных выражений, поэтому он использует . вместо ? для соответствия одному символу.

Если вы хотите просто проверить, что строка содержит подстроку, есть более классические способы сделать это, например,

# ${foo/b?r/} replaces "b?r" with the empty string in $foo
# So we're testing if $foo does not contain "b?r" one time
$ if [[ ${foo/b?r/} = $foo ]]; then echo "ok"; fi

Вы также можете проверить, начинается ли строка или заканчивается выражением следующим образом:

# ${foo%b?r} removes "bar" in the end of $foo
# So we're testing if $foo does not end with "b?r"
$ if [[ ${foo%b?r} = $foo ]]; then echo "ok"; fi

# ${foo#b?r} removes "b?r" in the beginning of $foo
# So we're testing if $foo does not begin with "b?r"
$ if [[ ${foo#b?r} = $foo ]]; then echo "ok"; fi
     ok

См. Расширение параметра параграф man bash для получения дополнительной информации об этих синтаксисах. Использование ## или %% вместо # и % соответственно приведет к получению самого длинного соответствия вместо простого соответствия.

Другой очень классический способ работы с подстановочными знаками - использовать case:

case $foo in 
   *bar)
       echo "Foo matches *bar"
       ;;
   bar?)
       echo "Foo matches bar?"
       ;;
   *)
       echo "Foo didn't match any known rule"
       ;;
esac
0 голосов
/ 23 декабря 2009

Ответ Джона Т был удален, но я действительно думаю, что он был на правильном пути. Вот оно:

Еще одним переносимым методом, который будет работать в большинстве версий bash, является чтобы повторить вашу строку, а затем трубку к grep. Если совпадение не найдено, оно будет оцените как ложное, поскольку результат будет пустым. Если что-то возвращается, это оценит как истинное.

[john@awesome]$string="Hello World"
[john@awesome]$if [[ `echo $string | grep Hello` ]];then echo "match";fi
match

Джон не учел подстановочный знак, запрошенный ответом. Для этого используйте egrep, a.k.a. grep -E, и , используйте подстановочный знак regex .*. Здесь . - это подстановочный знак, а * - это множитель, означающий «любое число из них». Итак, пример Джона становится:

$ string="Hello World"
$ if [[ `echo $string | egrep "Hel.*"` ]]; then echo "match"; fi

Подстановочный знак . является довольно стандартным регулярным выражением, поэтому он должен работать с любой командой, которая говорит регулярное выражение.

Это становится неприятным, если вам нужно экранировать специальные символы, так что это может быть неоптимальным:

$ if [[ `echo $string | egrep "\.\-\$.*"` ]]; then echo "match"; fi
...