Я новичок в написании скриптов оболочки и пытаюсь изучить основы. Я запускаю скрипт с помощью терминала, например, "./scriptgoeshere.sh Argument1 Argument2".
Я хочу, чтобы скрипт вывел какой-нибудь echo, "Hello World" - это, вероятно, лучший вариант для использования, но я хочу, чтобы он вывел hello world только если есть: Ровно два аргумента, и эти аргументы должны состоять исключительно из чисел. Например:
./scriptgoeshere.sh 1523 9643 ./scriptgoeshere.sh 7 96 Однако должен работать; ./scriptgoeshere.sh 594 arg НЕ должен работать, это скрипт, который у меня есть на данный момент, но он не работает. Я пытался разобраться с этим целый день, но, похоже, не могу приноровиться. Я даже пробовал if/elif/else, где он проверял, есть ли 2 входных данных в части if, и являются ли они оба числами в elif, прежде чем продолжить.
#!/bin/bash
if [ "$#" -eq 2 ] && [ "$@" -eq ^[0-9] ];
then
i=0
while [ $i -lt 3 ]
do
echo "Hello World!"
sleep 5
i=$((i+1))
done
else
echo "Need two arguments and only numbers will be accepted, such as ./scriptgoeshere 153 251"
fi
Он блокирует цикл hello world, пока я не введу 2 аргумента, но неважно, будут ли это числа или текст, и тогда я получаю ошибку: "./scriptgoeshere.sh: строка 2: [: ^[0-9]: ожидается целочисленное выражение". Я получаю эту ошибку, даже если пишу ./scriptgoeshere.sh 123 123 в терминале, чтобы запустить его. Я предпочитаю не решать ее с помощью переменных, так как хочу выяснить аргументы при запуске самого скрипта.
Это сводит меня с ума!
Это то, с чего я начал, чтобы заставить его проверять, есть ли ровно 2 аргумента, и это работает, я просто хочу добавить небольшое исправление, чтобы эти 2 аргумента ДОЛЖНЫ были числами. Я не понимаю, почему это кажется таким сложным.
#!/bin/bash
if [ "$#" -eq 2 ];
then
i=0
while [ $i -lt 3 ]
do
echo "Hello World!"
sleep 1
i=$((i+1))
done
else
echo "Need two argument, only numbers accepted"
fi
Редактировать: Я решил это. Я работал так близко.. Мне просто нужно было добавить дополнительные [] к числовой части строки if. Кто-нибудь знает, почему это нужно записывать как [[ "$@" -eq ^[0-9] ]], в то время как первая часть может быть записана всего одним [] ?
решение1
Сначала [ "$#" -eq ^[0-9] ]
или [ "$@" -eq ^[0-9] ]
(вопрос был отредактирован, пока я составлял свой ответ…). Из контекста я думаю, что вы хотите проверить, является ли параметр числом.
$#
всегда число, количество позиционных параметров в десятичном формате. Вы хотите проверить два позиционных параметра:$1
и$2
.[
является командой ([ whatever ]
эквивалентноtest whatever
). Это может быть встроенная команда оболочки, но синтаксически это какls
илиman
. Слова, которые следуют (включая]
), анализируются оболочкой как любые слова, которые следуют за командой: оболочка расширяет их (например,$#
становится числом), удаляет кавычки; в конечном итоге они становятся параметрами в[
. Одним из таких расширений является расширение имени файла, где различные шаблоны сопоставляются с существующими именами файлов (расширение имени файла, подстановка). Незаключенные в кавычки^[0-9]
могут расширяться до^2 ^5
, если эти файлы существуют. Если совпадений нет или если вы отключите функцию, то она^[0-9]
получит до[
(как один из ее аргументов) буквально. Выглядит как шаблон, но[
в любом случае не поддерживает сопоставление с шаблоном.-eq
требует, чтобы предыдущий и следующий аргументы были целыми числами.$#
расширяется до целого числа наверняка. В этом контексте нет способа^[0-9]
расшириться до числа.Двойные кавычки
$@
— это нечто особенное, они могут расширяться до нескольких слов («полей» в терминах POSIX), несмотря на то, что заключены в кавычки. Обычно мы заключаем их в кавычки, чтобы предотвратить разбиение слов:"$foo"
расширяется до одного слова, даже если значение содержит пробелы и т. п."$@"
расширяется как"$1" "$2" …
, его двойные кавычки не позволяют$1
расширяться до нескольких слов (и по отдельности$2
и т. д.). В этом фрагменте:[ "$#" -eq 2 ] && [ "$@" -eq ^[0-9] ]
второй
[
запускается, если первый успешен (так&&
работает), т.е. если есть ровно два позиционных параметра. В таком случае"$@"
эквивалентно"$1" "$2"
и второй тест становится[ "$1" "$2" -eq ^[0-9] ]
Это не имеет смысла.
[
Может жаловаться по-разному в зависимости от фактических значений параметров и расширения^[0-9]
, но все равно это мусор.
Теперь о [[ "$#" = [0-9] ]]
.
Примечание: этот фрагмент был в первой редакции вопроса. Вопрос был отредактирован, пока я составлял свой ответ. Я решил сохранить этот фрагмент, потому что он [[
может пригодиться в конечном итоге в любом случае.
- Еще раз:
$#
всегда число. [[
не эквивалентно[
. Любой разумный тест, который вы можете сделать с помощью ,[
можно переписать с помощью[[
(хотя не стоит делать это бездумно, просто заменяя[
на[[
). Есть дополнительные тесты[[
, которые можно сделать, это более мощно. Хотя[
(иtest
) требуется POSIX,[[
это не так.Важно:
[[
это не команда, какls
или[
. Это ключевое слово, оно изменяет способ, которым оболочка анализирует слова, которые следуют за ней. Все, что находится между[[
и]]
анализируется по-разному.Если оператор —
=
, то следующим аргументом является шаблон. Некоторые символы/синтаксисы (например,*
или[0-9]
), если они не заключены в кавычки, сопоставляются с аргументом перед=
. Это похоже на расширение имени файла, но не с существующими именами файлов. Ваш код проверял, если (расширенное)$#
— это ровно одна цифра. Весь тест был[[ "$#" -eq 2 ]] && [[ "$#" = [0-9] ]]
foo && bar
запускаетсяbar
, еслиfoo
успешно. Второе[[
было проверено только если$#
расширено до2
, поэтому оно должно было быть успешным, поскольку2
это ровно одна цифра. В этом случае второе[[
ничего не меняет.
Допустим, вы осознали свою ошибку и проверили, является ли $1
это числом.
- Как и выше,
[[ "$1" = [0-9] ]]
возвращает успех, если$1
расширяется ровно до одной цифры. [[ "$1" = [0-9][0-9] ]]
– ровно две цифры.[[ "$1" = [0-9]* ]]
– что-то, начинающееся с цифры.
Чтобы обнаружить любое количество цифр, вам нужно =~
. Этот оператор внутри [[ ]]
чем-то похож на =
, но теперь шаблон представляет собой расширенное регулярное выражение. Ему не нужно соответствовать всей строке слева от оператора, достаточно подстроки (поэтому ^
и $
якоря нужны в вашем случае):
[[ "$1" =~ [0-9] ]]
возвращает результат, если$1
расширяется до строки, содержащей цифру.[[ "$1" =~ [0-9][0-9] ]]
– … содержащий две цифры, идущие рядом.[[ "$1" =~ ^[0-9]$ ]]
– строка, состоящая ровно из одной цифры.[[ "$1" =~ ^[0-9]*$ ]]
– ноль или более цифр.[[ "$1" =~ ^[0-9][0-9]*$ ]]
– одна или несколько цифр.[[ "$1" =~ ^[0-9]+$ ]]
– одна или несколько цифр.
[[ "$1" =~ ^[0-9]+$ ]]
и [[ "$2" =~ ^[0-9]+$ ]]
какие тесты вам могут понадобиться.Чтобы разрешить лидировать, -
измените их следующим образом: [[ "$1" =~ ^-?[0-9]+$ ]]
Весь фрагмент тестирования может быть
[ "$#" -eq 2 ] && `[[ "$1" =~ ^-?[0-9]+$ ]]` && `[[ "$2" =~ ^-?[0-9]+$ ]]`
Внутренние [[ ]]
переменные/параметры не могут быть заключены в двойные кавычки, и их расширенные значения не будут подвергаться разбиению на слова и подстановке. Так что вы можете писать, [[ $1 =~ ^-?[0-9]+$ ]]
и это безопасно. Это исключение; в общем случае вам следует заключать переменные в двойные кавычки. Здесь двойные кавычки ничему не мешают, поэтому я советую в любом случае использовать кавычки, чтобы привыкнуть к этой хорошей общей практике.
Вы утверждаете, что решили проблему с помощью [[ "$@" -eq ^[0-9] ]]
. Извините, я вам не верю. Если есть ровно два позиционных параметра и они являются числами, я не вижу способа сделать это синтаксически правильным, не говоря уже о проверке того, что вы хотите. В других случаях… я все еще не вижу способа.
… используя [[ "$@" =~ ^[0-9] ]]
(из вашего комментария, не соответствующего вопросу). Это неправильное решение. Мои тесты показывают, что "$@"
внутри [[ ]]
ведет себя как не заключенное в кавычки, $@
рассматриваемое как одно слово без разбиения слов и подстановки. Это очень похоже на то, как ведут себя обычные переменные внутри [[ ]]
, хотя это не было очевидно для меня заранее, потому что я знаю, "$@"
что это не похоже на обычные переменные.
Примечание: по этой причине я никогда не хотел использовать его "$@"
внутри [[ ]]
. Я не могу представить себе сценарий, когда это было бы правильным.
Когда есть ровно два позиционных параметра, ваше "решение" становится [[ "$1 $2" =~ ^[0-9] ]]
. Оно синтаксически верно и проверяет, является ли самый первый символ первого аргумента вашего скрипта цифрой. Только это. Запустите ./scriptgoeshere.sh 0h well
, аргументы пройдут тест.
Примечания:
[0-9]
(как шаблон подстановки или регулярное выражение) зависит от вашей текущей локали. Скорее всего, это будет работать так, как вы ожидаете, но технически возможно иметь локаль, где9
предшествует0
или где2
находится за0-9
. Самый общий способ сопоставления с цифрой —[[:digit:]]
. Смотритеэтот.С другой стороны, вы можете захотеть использовать
$1
and/or$2
с арифметикой оболочки ($((…))
),[ "$1" -ge 50 ]
или что-то в этом роде. Тогда вам нужны только арабские цифры. Даже если[[:digit:]]
включает их в вашей локали (вероятно, так и есть), он может включать и другие цифры. Рассмотрите[0123456789]
; он явный и не зависит от локали.Не каждая совпадающая строка
^-?[0-9]+$
считается целым числом с точки зрения[
и/или[[
(например, когда вы делаете[ "$1" -eq 0 ]
). У каждого инструмента есть диапазон целых чисел, с которыми он может работать. Аналогично арифметика оболочки может выдавать математически неверные результаты, если вы снабжаете ее строкой цифр, описывающей целое число, выходящее за пределы диапазона.Математически
1.00
это целое число. Некоторые инструменты могут принять его как целое число, некоторые — нет. А как насчет1e100
?