Вот мой код
vmname = "$1"
EXCEPTLIST = "desktop-01|desktop-02|desktop-03|desktop-04"
if [[ $vmname != @(${EXCEPTLIST}) ]]; then
echo "${vmname}"
else
echo "Its in the exceptlist"
fi
Приведенный выше код работает отлично, но мой вопрос: EXCEPTLIST может быть длинной строкой, скажем, 100 имен серверов. В этом случае трудно поместить все эти имена в одну строку. В этой ситуации есть ли способ сделать переменную EXCEPTLIST многострочной? что-то вроде следующего:
EXCEPTLIST = "desktop-01|desktop-02|desktop-03| \n
desktop-04|desktop-05|desktop-06| \n
desktop-07|desktop-08"
Я не уверен, но думал о возможностях.
Видимо, я хотел бы узнать терминологию использования @(${})- Это называется расширением переменных или как? Кто-нибудь знает документацию/объясните мне, как это работает в bash. ?
Обратная косая черта, сразу за которой следует возврат каретки, обозначает строку продолжения. Однако для проверки того, является ли одна строка частью списка, я бы предпочел использовать ассоциативный массив (где его ключи являются элементом списка) вместо одной строки.
Возможный дубликат Проверить, существует ли переменная в списке в Bash





Я бы не стал заморачиваться с несколькими строками текста. Это было бы просто прекрасно:
EXCEPTLIST='desktop-01|desktop-02|desktop-03|'
EXCEPTLIST+='desktop-04|desktop-05|desktop-06|'
EXCEPTLIST+='desktop-07|desktop-08'
Конструкция @(...) называется расширенным шаблоном подстановки, и то, что она делает, является расширением того, что вы, вероятно, уже знаете, — подстановочных знаков:
VAR='foobar'
if [[ "$VAR" == fo?b* ]]; then
echo "Yes!"
else
echo "No!"
fi
Краткое руководство по расширенным примерам подстановок: https://www.linuxjournal.com/content/bash-extended-globbing
Можно объявить массив, если данные/строка длинные/большие. Используйте IFS и printf для строки формата, например:
#!/usr/bin/env bash
exceptlist=(
desktop-01
desktop-02
desktop-03
desktop-04
desktop-05
desktop-06
)
pattern=$(IFS='|'; printf '@(%s)' "${exceptlist[*]}")
[[ "$vmname" != $pattern ]] && echo good
В этой ситуации есть ли способ сделать переменную EXCEPTLIST многострочной?
С вашим заданным вводом/данными массив также является лучшим вариантом, например:
exceptlist=(
'desktop-01|desktop-02|desktop-03'
'desktop-04|desktop-05|desktop-06'
'desktop-07|desktop-08'
)
Проверьте, каково значение переменной $pattern одним из способов:
declare -p pattern
Выход:
declare -- pattern = "@(desktop-01|desktop-02|desktop-03|desktop-04|desktop-05|desktop-06)"
Нужно также проверить/проверить, является ли $vmname пустой строкой, так как она всегда будет истинной.
Кстати, не используйте все переменные верхнего регистра исключительно для внутренних целей.
$(...) называется Command Substitution.
LESS=+'/\ *Command Substitution' man bashВ дополнение к тому, что было упомянуто в комментариях о pattern matching
Смотрите LESS=+/'(pattern-list)' man bash
Смотрите LESS=+/' *\[\[ expression' man bash
Есть ли способ сделать переменную EXCEPTLIST многострочной?
Не вижу смысла использовать сопоставление. Используйте массив bash и просто сравните.
exceptlist=(
desktop-01
desktop-02
desktop-03
desktop-04
desktop-05
desktop-06
)
is_in_list() {
local i
for i in "${@:2}"; do
if [[ "$1" = "$i" ]]; then
return 0
fi
done
return 1
}
if is_in_list "$vmname" "${EXCEPTLIST[@]}"; then
echo "is in exception list ${vmname}"
fi
@(${})- Это называется расширением переменных или как? Кто-нибудь знает документацию/объясните мне, как это работает в bash. ?
${var} — переменное расширение.
@(...) — это просто символы @().
Из man bash в Compund commands:
[[ expression ]]
When the == and != operators are used, the string to the right of the operator is considered a pattern and matched according to the rules
described below under Pattern Matching, as if the extglob shell option were enabled. ...
Из Pattern Matching в man bash:
@(pattern-list)
Matches one of the given patterns
Команда [[ получает строку @(a|b|c), а затем сопоставляет аргументы.
Вот один из способов загрузить многострочную строку в переменную:
fn() {
cat <<EOF
desktop-01|desktop-02|desktop-03|
desktop-04|desktop-05|desktop-06|
desktop-07|desktop-08
EOF
}
exceptlist = "$(fn)"
echo $exceptlist
Что касается решения вашей конкретной проблемы, я могу придумать множество подходов.
Решение 1, поскольку все рабочие столы имеют одинаковый префикс desktop-0 и отличаются только последней буквой, мы можем использовать расширение {,} или {..} следующим образом:
vmname = "$1"
found=0
for d in desktop-{01..08}
do
if [[ "$vmname" == $d ]]; then
echo "It's in the exceptlist"
found=1
break
fi
done
if (( !found )); then
echo "Not found"
fi
Решение 2. Иногда полезно предоставить список в удобном для сопровождения текстовом списке. Мы можем использовать цикл while и перебирать список
vmname = "$1"
found=0
while IFS= read -r d
do
if [[ "$vmname" == $d ]]; then
echo "It's in the exceptlist"
found=1
break
fi
done <<EOF
desktop-01
desktop-02
desktop-03
desktop-04
desktop-05
desktop-06
desktop-07
desktop-08
EOF
if (( !found )); then
echo "Not found"
fi
Решение 3. Мы можем использовать серверы с помощью регулярных выражений:
vmname = "$1"
if [[ "$vmname" =~ ^desktop-0[1-8]$ ]]; then
echo "It's in the exceptlist"
else
echo "Not found"
fi
Решение 4. Мы заполняем массив, а затем перебираем массив:
vmname = "$1"
exceptlist=()
exceptlist+=(desktop-01 desktop-02 desktop-03 deskop-04)
exceptlist+=(desktop-05 desktop-06 desktop-07 deskop-08)
found=0
for d in ${exceptlist[@]}
do
if [[ "$vmname" == "$d" ]]; then
echo "It's in the exceptlist"
found=1
break;
fi
done
if (( !found )); then
echo "Not found"
fi
Вам не нужна found "логическая" переменная: for d in "${exceptlist[@]}"; do [[ "$vmname" == "$d" ]] && break; done && printf '%s is in the exceptlist\n' "$vmname"
Спасибо @LéaGris, когда я писал это, я мысленно думал как о случаях found, так и о случаях !found. Идея заключалась в том, чтобы показать, что он рассчитывается, но оставить его для адаптации на усмотрение ОП, но, учитывая ваши отзывы, я решил явно показать сообщения для обоих случаев.
Метод без булевой переменной также работает как для найденных, так и для ненайденных случаев if for d in "${exceptlist[@]}"; do [[ "$vmname" == "$d" ]] && break; done then printf '%s is in the exceptlist\n' "$vmname"; else printf '%s not found\n' "$vmname"; fi
#!/bin/bash
set +o posix
shopt -s extglob
vmname=$1
EXCEPTLIST=(
desktop-01 desktop-02 desktop-03
...
)
if IFS='|' eval '[[ ${vmname} == @(${EXCEPTLIST[*]}) ]]'; then
...
Нет абсолютно никакой необходимости использовать специфичные для Bash регулярное выражение или массивы и цикл для совпадения, если вы используете grep для необработанной строки на границе слова.
Список исключений может быть многострочным, тоже подойдет:
#!/usr/bin/sh
exceptlist='
desktop-01|desktop-02|desktop-03|
deskop-04|desktop-05|desktop-06|
desktop-07|deskop-08'
if printf %s "$exceptlist" | grep -qwF "$1"; then
printf '%s is in the exceptlist\n' "$1"
fi
Молодцы, что заставили это работать с grep. Обычно grep принимает регулярное выражение, что может привести к непреднамеренной обработке регулярных выражений, которую вы отключили с помощью -F. Другим предостережением является очистка входных данных, чтобы избежать непреднамеренных последовательностей, которых вы избегали, используя кавычки. Однако в вашем решении есть проблемы, когда "рабочий стол" и "01" отображаются как ложноположительные совпадения.
См. раздел «Сопоставление шаблонов» руководства bash:
@(pattern-list)Соответствует одному из заданных шаблонов.