Як я можу використовувати змінну як умову регістру?


17

Я намагаюся використовувати змінну, що складається з різних рядків, розділених тестом a |як caseтвердження. Наприклад:

string="\"foo\"|\"bar\""
read choice
case $choice in
    $string)
        echo "You chose $choice";;
    *)
        echo "Bad choice!";;
esac

Я хочу , щоб мати можливість ввести fooабо barі виконати першу частину caseзаяви. Однак і те, fooі barвзяти мене до другого:

$ foo.sh
foo
Bad choice!
$ foo.sh
bar
Bad choice!

Використання "$string"замість $stringне має значення. Не використовує string="foo|bar".

Я знаю, що можу це зробити так:

case $choice in
    "foo"|"bar")
        echo "You chose $choice";;
    *)
        echo "Bad choice!";;
esac

Я можу придумати різні способи вирішення, але хотілося б знати, чи можна використовувати змінну як caseумову в bash. Чи можливо це, і якщо так, то як?


2
Я не можу запропонувати собі це як справжню відповідь, але оскільки ніхто інший не згадав про це, ви можете зафіксувати випадок справи у eval, уникаючи $ вибір, паролі, зірочки, крапки з комою та нові рядки. Некрасиво, але це "працює".
Джефф Шаллер

@JeffSchaller - це не погана ідея багато разів, і, можливо, це лише квиток у цьому випадку. Я також подумав рекомендувати його, але readтрохи зупинив мене. на мій погляд, перевірка введення користувачем даних, яка, як видається, така, caseшаблони не повинні знаходитись у верхній частині списку оцінювання, а слід скористатись *схемою за замовчуванням, щоб гарантовано прийнятними були лише результати, які досягають там. все-таки, оскільки питання полягає в порядку розбору / розширення, то для цього вимагається друга оцінка.
mikeserv

Відповіді:


14

Посібник з bash говорить:

слово слова у списку [[(] шаблону [| шаблону] ...) ;; ] ... есак

Кожен досліджуваний візерунок розширюється за допомогою розширення тильди, розширення параметрів та змінних, арифметичної підстановки, підстановки команд та заміни процесу.

Ні «Розширення траєкторії»

Таким чином: шаблон НЕ розширюється за допомогою «Розширення Pathname».

Тому: шаблон НЕ може містити "|" всередині. Тільки: два шаблони можна було з'єднати із знаком "|".

Це працює:

s1="foo"; s2="bar"    # or even s1="*foo*"; s2="*bar*"

read choice
case $choice in
    $s1|$s2 )     echo "Two val choice $choice"; ;;  # not "$s1"|"$s2"
    * )           echo "A Bad  choice! $choice"; ;;
esac

Використання «Extended Globbing»

Однак wordвін узгоджується з patternвикористанням правил «Pathname Expansion».
І «Extended Globbing» тут , тут і тут дозволяє використовувати чергування ("|") шаблонів.

Це також працює:

shopt -s extglob

string='@(foo|bar)'

read choice
    case $choice in
        $string )      printf 'String  choice %-20s' "$choice"; ;;&
        $s1|$s2 )      printf 'Two val choice %-20s' "$choice"; ;;
        *)             printf 'A Bad  choice! %-20s' "$choice"; ;;
    esac
echo

Рядок змісту

Наступний тестовий скрипт показує, що візерунок, який відповідає всім рядкам, які містять або є, fooабо barбудь-де, '*$(foo|bar)*'або дві змінні $s1=*foo*та$s2=*bar*


Сценарій тестування:

shopt -s extglob    # comment out this line to test unset extglob.
shopt -p extglob

s1="*foo*"; s2="*bar*"

string="*foo*"
string="*foo*|*bar*"
string='@(*foo*|*bar)'
string='*@(foo|bar)*'
printf "%s\n" "$string"

while IFS= read -r choice; do
    case $choice in
        "$s1"|"$s2" )   printf 'A first choice %-20s' "$choice"; ;;&
        $string )   printf 'String  choice %-20s' "$choice"; ;;&
        $s1|$s2 )   printf 'Two val choice %-20s' "$choice"; ;;
        *)      printf 'A Bad  choice! %-20s' "$choice"; ;;
    esac
    echo
done <<-\_several_strings_
f
b
foo
bar
*foo*
*foo*|*bar*
\"foo\"
"foo"
afooline
onebarvalue
now foo with spaces
_several_strings_

9

Ви можете скористатися extglobопцією:

shopt -s extglob
string='@(foo|bar)'

Цікаво; що робить @(foo|bar)особливим порівняно з foo|bar? Обидва є дійсними шаблонами, які працюють однаково при буквальному введенні.
чепнер

4
Ах, неважливо. |не є частиною шаблону в foo|bar, це частина синтаксису caseоператора, щоб дозволити кілька шаблонів в одному пункті. Хоча | це частина розширеного шаблону.
чепнер

3

Вам потрібні дві змінні, caseоскільки або |труба розбирається перед розширенням шаблонів.

v1=foo v2=bar

case foo in ("$v1"|"$v2") echo foo; esac

foo

Шаблони оболонок у змінних обробляються по-різному, коли їх також цитують або цитують:

q=?

case a in
("$q") echo question mark;;
($q)   echo not a question mark
esac

not a question mark

-1

Якщо ви хочете обробляти сумісні штрихи, ви можете написати:

  string="foo|bar"
  read choice
  awk 'BEGIN{
   n=split("'$string'",p,"|");
   for(i=1;i<=n;i++)
    if(system("\
      case \"'$choice'\" in "p[i]")\
       echo \"You chose '$choice'\";\
       exit 1;;\
      esac"))
     exit;
   print "Bad choice"
  }'

Пояснення: awk використовується для розділення "рядка" та перевірки кожної частини окремо. Якщо "вибір" відповідає тестованій на даний момент частині p [i], команда awk завершиться "вихід" у рядку 11. Для самого тесту використовується "випадок" оболонки (в рамках "виклику системи") , на запитання тердона. Це зберігає можливість змінити призначений тест- "рядок", наприклад, на "foo * | bar", щоб відповідати також "foooo" ("шаблон пошуку"), як дозволяє "випадок" оболонки. Якщо замість цього ви віддаєте перевагу регулярним виразам, ви можете опустити "system" -call і використовувати замість awk "~" або "match".


Шановний молодший я, я міг би краще дізнатися, як уникнути марних кандидатів на рішення, якщо ти скажеш мені причину твого голосування.
Джеральд Шаде
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.