#linux #string #bash
#linux #строка #bash
Вопрос:
Как я могу это сделать в bash? И можно ли изменить [[ $ 1 =~ apple amp;amp; $ 1 =~ banana ]] на что-то вроде [[ $ 1 = ~ apple amp;amp;banana ]] ?
testme() {
if [[ $1 =~ apple amp;amp; $1 =~ banana ]]; then
echo 'contains both words'
fi
}
testme 'apple banana' #should echo 'contains both words'
testme ' apple banana' #should echo 'contains both words'
testme 'apple banana '#should echo 'contains both words'
testme ' applebanana' #should echo nothing
testme 'apple cherry
banana
pear' #separated by newline should echo 'contains both words'
testme 'apples bananas' #should echo nothing
Комментарии:
1. а как насчет
banana apple
?2. Замедляйтесь, по одному вопросу за раз — ваша проблема в том, что регулярное выражение не выполняет то, что вы хотите? Все строки содержат подстроки «apple» и «banana», поэтому теоретически все они должны возвращать «содержит оба слова»
3. Технически, с оригинальным (состоящим из 2 частей) тестом «bananapple» также будет соответствовать.
4. Что касается размещения амперсандов, нет, к сожалению, амперсанды разделяют полные предложения — тест bash ‘banana’ даст True, предлагаемое вами изменение будет эквивалентно чему-то вроде ‘if [[$ 1 = ~ apple ]] amp;amp; [[ banana ]]`
5. Похоже, вам нужны границы слов, которые не поддерживаются расширенными регулярными выражениями POSIX, используемыми bash.
Ответ №1:
Как комментирует @Shawn, регулярные выражения bash не поддерживают границы слов. grep выполняет:
testme() { grep -qFzw -e apple -e banana <<<"$1" amp;amp; echo "contains both words"; }
Это проходит все ваши тесты, а также banana, предшествующий apple.
Если вы хотите придерживаться bash, вот не совсем ужасный способ
word_re() {
local nonword='[^[:alnum:]_]'
printf '(^|%s)%s($|%s)' "$nonword" "$1" "$nonword"
}
testme() {
if [[ $1 =~ $(word_re apple) amp;amp; $1 =~ $(word_re banana) ]]; then
echo "contains both words"
fi
}
или, если у вас есть список слов:
testme() {
for word in apple banana orange kumquat; do
[[ $1 =~ $(word_re "$word") ]] || return
done
echo "contains ALL words"
}
Комментарии:
1. Я собирался нажать post для того, который использует perl, но мне больше нравится подход grep.
2. Да, добавьте достаточно опций в grep, и что-то застрянет.
3. Хорошо, большое вам спасибо! Я хотел обойти дополнительный вызов grep и перенаправление stdin, но, к сожалению, bash =~ не поддерживает это.
4. Просто хочу отметить, что для работы вам нужно дважды вызвать grep
5. grep -qFzw -e ‘apple’ <<< «$1» amp;amp; grep -qFzw -e ‘банан’ <<< «$1» amp;amp; echo «содержит оба слова»
Ответ №2:
Хотя bash
собственные регулярные выражения не поддерживают границы слов, как вы хотите (без каких-либо более уродливых, чем обычно, обручей), если вы можете использовать zsh
вместо этого, вы можете сделать это исключительно в этой оболочке без внешних программ, таких как grep
:
#!/usr/bin/env zsh
zmodload -F zsh/pcre
setopt REMATCH_PCRE
testme() { [[ $1 =~ \bapple\b amp;amp; $1 =~ \bbanana\b ]] amp;amp; echo "contains both words"; }
testme 'apple banana' #should echo 'contains both words'
testme ' apple banana' #should echo 'contains both words'
testme 'apple banana '#should echo 'contains both words'
testme ' applebanana' #should echo nothing
testme 'apple cherry
banana
pear' #separated by newline should echo 'contains both words'
testme 'apples bananas' #should echo nothing
Это позволяет zsh
использовать библиотеку PCRE для сопоставления регулярных выражений на диалекте perl вместо расширенного разрешения Posix по умолчанию. (Ваша версия zsh
, должно быть, была скомпилирована с поддержкой PCRE; я не знаю, есть ли все распространенные zsh
пакеты ОС; Ubuntu есть).
b
perl
повторный синтаксис для ‘совпадения по границе слова’ (обратите внимание на необходимость удвоить обратную косую черту, чтобы хорошо играть с оболочкой).
И для полноты, версии testme
функции, которые используют другие языки для сопоставления (хотя я думаю grep
, что версия Гленна превосходит обе в этом случае).
perl
:
testme() {
perl -0777 -nE 'say "contains both words" if /bappleb/ and /bbananab/' <<<"$1"
}
и чтобы проиллюстрировать еще один вариант регулярного выражения tcl
(который используется m
для привязок начала слова и M
конца слова):
testme() {
STR="$1" tclsh <<<'
if {[regexp {mappleM} $env(STR)] amp;amp; [regexp {mbananaM} $env(STR)]} {
puts "contains both words"
}'
}