Перевірте кількість аргументів, переданих сценарію Bash


727

Я хотів би, щоб мій сценарій Bash надрукував повідомлення про помилку, якщо необхідний кількість аргументів не виконано.

Я спробував наступний код:

#!/bin/bash
echo Script name: $0
echo $# arguments 
if [$# -ne 1]; 
    then echo "illegal number of parameters"
fi

З незрозумілої причини у мене виникла така помилка:

test: line 4: [2: command not found

Що я роблю неправильно?


54
Не слід називати свій сценарій test. Це назва стандартної команди Unix, ви не хочете її затінювати.
Бармар

20
Завжди використовуйте пробіли навколо '[' ('[[') або '(' ('((')), якщо висловлювання в bash.
zoska

5
Щоб додати до коментаря @zoska, вам знадобиться пробіл перед [тому, що він реалізований як команда, спробуйте "який ['.
Даніель Да Кунья

1
кращий приклад наведено на посилання нижче: stackoverflow.com/questions/4341630 / ...
Ramkrishna

3
@Barmar, безумовно, називає це testдобре, поки він не на ПАТІ?
користувач253751

Відповіді:


1099

Як і будь-яка інша проста команда, [ ... ]або testвимагає пробілів між її аргументами.

if [ "$#" -ne 1 ]; then
    echo "Illegal number of parameters"
fi

Або

if test "$#" -ne 1; then
    echo "Illegal number of parameters"
fi

Пропозиції

Перебуваючи в Bash, віддайте перевагу використанню [[ ]]натомість, оскільки це не робить розбиття слів та розширення імені шляху до його змінних, що цитування може бути не потрібним, якщо це не є частиною виразу.

[[ $# -ne 1 ]]

Він також має деякі інші функції, такі як групування умов, що не котируються, відповідність шаблонів (розширена відповідність шаблону extglob) та збігання з регулярними виразами.

Наступний приклад перевіряє, чи аргументи є достовірними. Це дозволяє один аргумент або два.

[[ ($# -eq 1 || ($# -eq 2 && $2 == <glob pattern>)) && $1 =~ <regex pattern> ]]

Для чистих арифметичних виразів, з допомогою (( ))деяких все ще може бути краще, але вони все ще можливі [[ ]]з його арифметичними операторами подобаються -eq, -ne, -lt, -le, -gt, або -geшлях розміщення вираження у вигляді одного рядка аргументу:

A=1
[[ 'A + 1' -eq 2 ]] && echo true  ## Prints true.

Це може бути корисно, якщо вам також потрібно буде поєднувати його з іншими особливостями [[ ]].

Вихід із сценарію

Також логічно зробити сценарій виходу, коли йому передаються недійсні параметри. Це вже було запропоновано в коментарі по ekangas , але хто - то редагував цей відповідь , щоб мати його в -1якості значення, що повертається, так що я міг би також зробити це правильно.

-1хоч прийнятий Башем як аргумент не exitє явно задокументованим і не може бути використаний як загальна пропозиція. 64також є найбільш формальним значенням , так як це визначено в sysexits.hс #define EX_USAGE 64 /* command line usage error */. Більшість таких інструментів lsтакож повертаються 2до недійсних аргументів. Я також повертався 2у своїх сценаріях, але останнім часом мене вже не цікавлять, а просто використовуються 1у всіх помилках. Але давайте просто розмістимо 2тут, оскільки це найчастіше і, мабуть, не стосується ОС.

if [[ $# -ne 1 ]]; then
    echo "Illegal number of parameters"
    exit 2
fi

Список літератури


2
ОП: Майте на увазі, що [це лише інша команда, тобто спробуйте which [.
Лев

5
@Leo Команди можуть бути вбудовані, а не можуть бути. У bash, [це вбудований, в той час [[як ключове слово. У деяких старих оболонках [навіть не вбудований. Команди на зразок [природно співіснують як зовнішня команда у більшості систем, але внутрішні команди надають пріоритет оболонці, якщо ви не обходите з commandабо exec. Перегляньте документацію оболонки щодо їх оцінки. Візьміть до уваги їхню різницю та те, як вони можуть поводитися по-різному в кожній оболонці.
konsolebox

77

Можливо, буде корисно використовувати арифметичні вирази, якщо ви маєте справу з числами.

if (( $# != 1 )); then
    echo "Illegal number of parameters"
fi

Чому це може бути хорошою ідеєю в даному випадку? З огляду на ефективність, портативність та інші проблеми, чи не найкраще використовувати найпростіший і загальновизнаний синтаксис, тобто [ ... ], коли це робить роботу добре, і не потрібні химерні операції?
Макс

Арифметичні розширення @Max $(( ))не фантастичні, і їх слід реалізувати всі оболонки POSIX. Однак (( ))синтаксис (без $) не є його частиною. Якщо ви з якоїсь причини обмежені, то напевно можете використовувати [ ]натомість, але майте на увазі, що тоді ви також не повинні використовувати [[ ]]. Я сподіваюся, що ви зрозуміли підводні камені [ ]та причини, чому ці особливості існують. Але це було питання Баша, тому ми даємо відповіді Баша ( "Як правило, [[використовується для рядків і файлів. Якщо ви хочете порівняти числа, використовуйте ArithmeticExpression" ).
Алекс-Даніель Якименко-А.

39

На []:! =, =, == ... є рядок операторами порівняння і -eq, -gt ... є арифметичними двійковими.

Я б використав:

if [ "$#" != "1" ]; then

Або:

if [ $# -eq 1 ]; then

9
==насправді недокументована функція, яка трапляється працювати з GNU test. Також трапляється працювати з FreeBSD test, але може не працювати в foo test . Тільки стандартне порівняння =(просто FYI).
Martin Tournoij

1
Це задокументовано при записі bash man: Коли використовуються оператори == і! =, Рядок праворуч від оператора вважається шаблоном і узгоджується згідно з правилами, описаними нижче в розділі Узгодження шаблону. Якщо опція оболонки nocasematch увімкнена, збіг виконується без огляду на регістр буквених символів. Повернене значення дорівнює 0, якщо рядок відповідає (==) або не відповідає (! =) Шаблону, а 1 - інакше. Будь-яка частина шаблону може бути процитована, щоб змусити його відповідати як рядок.
jhvaras

2
@jhvaras: Саме так сказав Carpetsmoker: він може працювати в деяких реалізаціях (і справді він працює в Bash), але це не сумісно з POSIX . Наприклад, він буде терпіти невдачі з dash: dash -c '[ 1 == 1 ]'. POSIX вказує лише =, а не ==.
gniourf_gniourf

34

Якщо вас цікавить лише те, що певний аргумент відсутній, заміщення параметрів чудово:

#!/bin/bash
# usage-message.sh

: ${1?"Usage: $0 ARGUMENT"}
#  Script exits here if command-line parameter absent,
#+ with following error message.
#    usage-message.sh: 1: Usage: usage-message.sh ARGUMENT

хіба це не завантажено басизмами?
Дуайт Спенсер

@DwightSpencer Було б це важливо?
konsolebox

@Temak Я можу, якщо у вас є конкретні запитання, але ця стаття пояснює це краще, ніж я можу.
Пат

13

Простий один вкладиш, який працює, можна зробити за допомогою:

[ "$#" -ne 1 ] && ( usage && exit 1 ) || main

Це розділяється на:

  1. перевірити змінну bash на розмір параметрів $ # не дорівнює 1 (наша кількість підкоманд)
  2. якщо це правда, то функція використання () дзвінка та вихід із статусом 1
  3. інакше викликайте функцію main ()

Думає відзначити:

  • використання () може бути простим відлунням "$ 0: парами"
  • Основним може бути один довгий сценарій

1
Якщо у вас є інший набір рядків після цього рядка, це було б неправильно, оскільки exit 1застосовуватиметься лише до контексту нижньої частини, роблячи його просто синонімом ( usage; false ). Я не прихильник такого способу спрощення, коли мова йде про параметри аналізу, але ви можете використовувати { usage && exit 1; }замість цього. Або, мабуть, просто { usage; exit 1; }.
konsolebox

1
@konsolebox (використання && вихід 1) працює для ksh, zsh та bash, повертаючись до bash 2.0. Синтаксис {...} лише недавній для 4.0+ bash. Не зрозумійте мене неправильно, якщо один із способів працює добре для вас, тоді використовуйте його, але пам’ятайте, що не кожен використовує ту саму реалізацію bash, яку ви робите, і ми повинні кодувати, щоб розміщувати стандарти, а не башизми.
Дуайт Спенсер

Я не впевнений, що ти кажеш. {...}є загальним синтаксисом і доступний для більшості, якщо не всіх оболонок на основі sh, навіть тих старих оболонок, які не відповідають стандартам POSIX.
konsolebox

7

Перевірте це шахрайським шаблоном, він може допомогти багато.

Щоб перевірити довжину переданих аргументів, ви використовуєте "$#"

Для використання масиву переданих аргументів ви використовуєте "$@"

Прикладом перевірки довжини та повторення може бути:

myFunc() {
  if [[ "$#" -gt 0 ]]; then
    for arg in "$@"; do
      echo $arg
    done
  fi
}

myFunc "$@"

Ця стаття допомогла мені, але пропустила кілька речей для мене та моєї ситуації. Сподіваємось, це комусь допомагає.


0

У випадку, якщо ви хочете бути в безпеці, рекомендую скористатися getopts.

Ось невеликий приклад:

    while getopts "x:c" opt; do
      case $opt in
        c)
          echo "-$opt was triggered, deploy to ci account" >&2
          DEPLOY_CI_ACCT="true"
          ;;
            x)
              echo "-$opt was triggered, Parameter: $OPTARG" >&2 
              CMD_TO_EXEC=${OPTARG}
              ;;
            \?)
              echo "Invalid option: -$OPTARG" >&2 
              Usage
              exit 1
              ;;
            :)
              echo "Option -$OPTARG requires an argument." >&2 
              Usage
              exit 1
              ;;
          esac
        done

див. докладніші відомості тут, наприклад http://wiki.bash-hackers.org/howto/getopts_tutorial


0

Тут простий один вкладиш, щоб перевірити, чи вказано лише один параметр, інакше вийдіть із сценарію:

[ "$#" -ne 1 ] && echo "USAGE $0 <PARAMETER>" && exit

-1

Ви повинні додати пробіли між умовою тесту:

if [ $# -ne 1 ]; 
    then echo "illegal number of parameters"
fi

Я сподіваюся, що це допомагає.

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