Проверьте количество аргументов, переданных скрипту Bash

729

Я хотел бы, чтобы мой сценарий 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
Всегда используйте пробелы вокруг '[' ('[[') или '(' ('((')) в операторах if в bash.
zoska
5
Чтобы добавить к комментарию @zoska, перед пробелом должен быть пробел [, поскольку он реализован в виде команды, попробуйте 'which ['.
Даниэль Да Кунья
1
лучший пример приведен по ссылке ниже: stackoverflow.com/questions/4341630/…
ramkrishna
3
@ Бармар, конечно, называет это testнормально, если его нет в PATH?
user253751

Ответы:

1101

Как и любая другая простая команда, [ ... ]или 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хотя принятый Bash в качестве аргумента, 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

Ссылки

konsolebox
источник
2
ОП: Имейте в виду, что [это просто другая команда, то есть попытка which [.
Лев
5
Команды @Leo могут быть встроены, а могут и нет. В bash [это встроенное, а [[ключевое слово. В некоторых старых оболочках [даже не встроено. Такие команды, как, [естественно, сосуществуют как внешние команды в большинстве систем, но внутренние команды имеют приоритет над командной оболочкой, если вы не используете commandили exec. Проверьте документацию оболочки о том, как они оценивают. Обратите внимание на их разницу и то, как они могут вести себя по-разному в каждой оболочке.
konsolebox
79

Это может быть хорошей идеей использовать арифметические выражения, если вы имеете дело с числами.

if (( $# != 1 )); then
    echo "Illegal number of parameters"
fi
Алекс-Даниил Якименко-А.
источник
Почему это может быть хорошей идеей в данном случае? Учитывая эффективность, переносимость и другие вопросы, не лучше ли использовать самый простой и общепризнанный синтаксис, т. Е. [ ... ]Когда это просто отлично справляется с работой и не требует никаких причудливых операций?
Макс
Арифметические расширения @Max $(( ))не причудливы и должны быть реализованы всеми оболочками POSIX. Однако (( ))синтаксис (без $) не является его частью. Если вы по какой-то причине ограничены, то, конечно, вы можете использовать [ ]вместо этого, но имейте в виду, что тогда вы также не должны использовать [[ ]]. Я надеюсь, что вы понимаете подводные камни [ ]и причины, по которым эти функции существуют. Но это был вопрос Bash, поэтому мы даем ответы Bash ( «Как правило, [[используется для строк и файлов. Если вы хотите сравнить числа, используйте выражение ArithmeticExpression» ).
Алекс-Даниил Якименко-А.
40

On []:! =, =, == ... - операторы сравнения строк, а -eq, -gt ... - арифметические двоичные операторы .

Я хотел бы использовать:

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

Или:

if [ $# -eq 1 ]; then
jhvaras
источник
9
==на самом деле не документировано, что случается работать с GNU test. Это также случается работать с FreeBSD test, но может не работать на обув test . Только стандартное сравнение =(просто FYI).
Мартин Турной
1
Это описано в записи bash man: Когда используются операторы == и! =, Строка справа от оператора считается шаблоном и сопоставляется в соответствии с правилами, описанными ниже в разделе «Сопоставление с шаблоном». Если включена опция оболочки nocasematch, сопоставление выполняется без учета буквенных символов. Возвращаемое значение равно 0, если строка соответствует (==) или не соответствует (! =) Шаблону, и 1 в противном случае. Любая часть шаблона может быть заключена в кавычки, чтобы ее можно было сопоставить как строку.
Джхварас
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 Я могу, если у вас есть конкретные вопросы, но статья, на которую есть ссылки, объясняет это лучше, чем я.
Pat
13

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

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

Это разбивается на:

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

Думает отметить:

  • Использование () может быть просто эхо "$ 0: params"
  • Основным может быть один длинный скрипт
Дуайт Спенсер
источник
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, что и вы, и мы должны написать код, чтобы установить стандарты, а не bashisms.
Дуайт Спенсер
Я не уверен, что ты говоришь. {...}является распространенным синтаксисом и доступен большинству, если не всем оболочкам sh, даже тем старым оболочкам, которые не соответствуют стандартам POSIX.
konsolebox
7

Проверьте это bash шпаргалку, это может помочь много.

Чтобы проверить длину передаваемых аргументов, вы используете "$#"

Чтобы использовать массив переданных аргументов, вы используете "$@"

Пример проверки длины и итерации:

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

IsaacE
источник
0

Здесь простые строки, чтобы проверить, задан ли только один параметр, иначе выйдите из скрипта:

[ "$#" -ne 1 ] && echo "USAGE $0 <PARAMETER>" && exit
panticz
источник
-1

Вы должны добавить пробелы между условиями теста:

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

Надеюсь, это поможет.

Фабрисио
источник