Bash-скрипт с многострочной переменной

Вот мой код

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: @(pattern-list) Соответствует одному из заданных шаблонов.

jhnc 31.01.2023 08:07

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

user1934428 31.01.2023 08:25

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

Léa Gris 31.01.2023 11:30
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
3
117
6
Перейти к ответу Данный вопрос помечен как решенный

Ответы 6

Я бы не стал заморачиваться с несколькими строками текста. Это было бы просто прекрасно:

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éa Gris 31.01.2023 10:59

Спасибо @LéaGris, когда я писал это, я мысленно думал как о случаях found, так и о случаях !found. Идея заключалась в том, чтобы показать, что он рассчитывается, но оставить его для адаптации на усмотрение ОП, но, учитывая ваши отзывы, я решил явно показать сообщения для обоих случаев.

Stephen Quan 31.01.2023 22:03

Метод без булевой переменной также работает как для найденных, так и для ненайденных случаев 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

Léa Gris 31.01.2023 22:57
#!/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" отображаются как ложноположительные совпадения.

Stephen Quan 31.01.2023 22:11

Другие вопросы по теме