Bash аргументы. BASH: Разбор параметров командной строки. Чтение данных из файла

getopts — встроенная команда Bash, используемая для разбора аргументов, передаваемых скрипту из командной строки. Она позволяет обрабатывать серии опций, объединенных в один аргумент и дополнительные аргументы, передаваемые опцией.

# script_name -abc -d /home/user

Вместе с командой getopts используются скрытые переменные: $OPTIND и $OPTARG.

Символ двоеточия, следующий за именем опции, указывает на то, что она имеет дополнительный аргумент.

While getopts ":abcde:fg" Option

Обычно getopts упаковывается в цикл while , в каждом проходе цикла извлекается очередная опция и ее аргумент (если он имеется), обрабатывается, затем уменьшается на 1 скрытая переменная $OPTIND и выполняется переход к началу новой итерации.

Опциям, передаваемым в сценарий из командной строки, должен предшествовать символ «минус» (-) или «плюс» (+). Этот префикс (- или +) позволяет getopts отличать опции от прочих аргументов. Фактически, getopts не будет обрабатывать аргументы, если им не предшествует символ — или +, выделение опций будет прекращено как только встретится первый аргумент.

Типичная конструкция цикла while с getopts несколько отличается от стандартной из-за отсутствия квадратных скобок, проверяющих условие продолжения цикла.
Можно смотреть как работает getopts с помощью следующего скрипта.

test-getopts

usage () {
echo «Скрипт `basename $0` предназначен для демонстрации возможностей getopts.»
echo «»
echo «Использование: `basename $0` -abef -c C -d D»
echo -e » \033 # Сценарий вызван без аргументов?
then
usage # Если запущен без аргументов — вывести справку
exit $E_OPTERROR # и выйти с кодом ошибки
fi

while getopts «abc:d:ef» Option
do
case $Option in
a | b) echo «Действие 1: опция — $Option. Номер опции: $OPTIND. Аргумент: $OPTARG»;;
c) echo «Действие 2: опция — $Option. Номер опции: $OPTIND. Аргумент: $OPTARG»;;
d) echo «Действие 3: опция — $Option. Номер опции: $OPTIND. Аргумент: $OPTARG»;;
e) echo «Действие 4: опция — $Option. Номер опции: $OPTIND. Аргумент: $OPTARG»;;
f) echo «Действие 5: опция — $Option. Номер опции: $OPTIND. Аргумент: $OPTARG»;;
*) echo «Выбран недопустимый ключ.»
usage
exit $E_OPTERROR;; # ПО-УМОЛЧАНИЮ
esac
done
shift $(($OPTIND — 1))

Попробуйте позапускать его так:

Test-getopts -abc с-opt
test-getopts -abefd d-opt

А теперь так:

Test-getopts -abcdef
test-getopts -cd d-opt

Связано такое поведение getopts с тем, что опция -c ожидает дополнительный аргумент.

Наверное, всем известно, что у оболочки Bash есть встроенные команды, которых нет в папках /bin или /usr/bin. Они встроены в оболочку и выполняются в виде функций. В одной из предыдущих статей мы рассматривали . Мы обговорили там почти все, как должны выглядеть скрипты, использование условий, циклов, переменных, но не останавливались на функциях.

В сегодняшней статье мы исправим этот недостаток. Как и в любом языке программирования, в Bash есть функции их может быть очень полезно использовать. Мы рассмотрим использование функций bash, как их писать и даже как создавать библиотеки из этих функций.

Сначала нужно понять что такое функция в нашем контексте. Функция - это набор команд, объединенных одним именем, которые выполняют определенную задачу. Функция вызывается по ее имени, может принимать параметры и возвращать результат работы. Одним словом, функции Bash работают так же, как и в других языках программирования.

Синтаксис создания функции очень прост:

имя_функции () { список_команд }

Имя функции не должно совпадать ни с одной из существующих команд или функций, а все команды в теле функции пишутся с новой строки.

Простая функция

Давайте напишем небольшую функцию, которая будет выводить строку на экран:

$ vi function.sh

#!/bin/bash
printstr(){
echo "hello world"
}
printstr

Вызов функции bash выполняется указанием ее имени, как для любой другой команды. Запустите наш скрипт на выполнение, не забывайте, что перед этим нужно дать ему права на выполнение:

chmod u+x function.sh

Все работает, теперь усложним задачу, попробуем передать функции аргументы.

Аргументы функции

Аргументы функции нужно передавать при вызове, а читаются они точно так же, как и аргументы скрипта. Синтаксис вызова функции с параметрами bash такой:

имя_функции аргумент1 аргумент2 ... аргументN

Как видите, все достаточно просто. Параметры разделяются пробелом. Теперь улучшим нашу функцию, чтобы она выводила заданную нами строку:

!/bin/bash
printstr(){
echo $1
}
printstr "Hello world"

Можно сделать, чтобы параметров было несколько:

!/bin/bash
printstr(){
echo $1
echo $2
echo $3
echo $5
}
printstr "arg1" "arg2" "arg3" "arg4" "arg5"

Есть и другой способ извлекать аргументы, как в Си, с помощью стека. Мы извлекаем первый аргумент, затем сдвигаем указатель стека аргументов на единицу и снова извлекаем первый аргумент. И так далее:

!/bin/bash
printstr(){
echo $1
shift
echo $1
shift
echo $1
shift
echo $1
}
printstr "arg1" "arg2" "arg3" "arg4"

Возврат результата функции

Вы можете не только использовать функции с параметрами bash, но и получить от нее результат работы. Для этого используется команда return. Она завершает функцию и возвращает числовое значение кода возврата. Он может быть от 0 до 255:

!/bin/bash
printstr(){
return 134;
}
printstr
echo $?

Если вам нужно применить возврат значения функции bash, а не статус код, используйте echo. Строка не сразу выводится в терминал, а возвращается в качестве результата функции и ее можно записать в переменную, а затем использовать:

!/bin/bash
printstr(){
echo "test"
}
VAR=$(printstr)
echo $VAR

Экспорт функций

Вы можете сделать функцию доступной вне скрипта с помощью команды declare:

!/bin/bash
printstr(){
echo "hello world"
}
declare -x -f printstr

Затем запустите скрипт с помощью команды source:

source function.sh
$ printstr

Рекурсия

Вы можете вызвать функцию из нее же самой, чтобы сделать рекурсию:

!/bin/bash
printstr(){
echo "hello world"
printstr
}
printstr

Вы можете поэкспериментировать с использованием рекурсии, во многих случаях это может быть полезным, только не забывайте делать первый вызов функции Bash.

Локальные переменные в функции

Если вы объявите обычную переменную в функции, то она будет доступной во всем скрипте, это удобно для возврата значения функции, но иногда может понадобиться сделать локальную переменную. Для этого существует команда local:

!/bin/bash
printstr(){
local VAR=$1
echo ${VAR}
}
printstr "Hello World"

Библиотеки функций

Мы можем взять некоторые функции bash и объединить их в одну библиотеку, чтобы иметь возможность одной командой импортировать эти функции. Это делается похожим образом на экспорт функций. Сначала создадим файл библиотеки:

test1(){
echo "Hello World from 1";
}
test2(){
echo "Hello World from 2";
}
test3(){
echo "Hello World from 3";
}

Теперь создадим скрипт, который будет использовать наши функции. Импортировать библиотеку можно с помощью команды source или просто указав имя скрипта:

!/bin/bash
source lib.sh
test1
test2
test3

Выводы

В этой статье мы рассмотрели функции bash, как их писать, применять и объединять в библиотеки. Если вы часто пишете скрипты на Bash, то эта информация будет для вас полезной. Вы можете создать свой набор функций, для использования их в каждом скрипте и тем самым облегчить себе работу.

Освоив предыдущие части этой серии материалов, вы узнали о том, что такое bash-скрипты, как их писать, как управлять потоком выполнения программы, как работать с файлами. Сегодня мы поговорим о том, как добавить скриптам интерактивности, оснастив их возможностями по получению данных от пользователя и по обработке этих данных.

Наиболее распространённый способ передачи данных сценариям заключается в использовании параметров командной строки. Вызвав сценарий с параметрами, мы передаём ему некую информацию, с которой он может работать. Выглядит это так:

$ ./myscript 10 20
В данном примере сценарию передано два параметра - «10» и «20». Всё это хорошо, но как прочесть данные в скрипте?

Чтение параметров командной строки

Оболочка bash назначает специальным переменным, называемым позиционными параметрами, введённые при вызове скрипта параметры командной строки:
  • $0 - имя скрипта.
  • $1 - первый параметр.
  • $2 - второй параметр - и так далее, вплоть до переменной $9 , в которую попадает девятый параметр.
Вот как можно использовать параметры командной строки в скрипте с помощью этих переменных:

#!/bin/bash echo $0 echo $1 echo $2 echo $3
Запустим сценарий с параметрами:

./myscript 5 10 15
Вот что он выведет в консоль.


Вывод параметров, с которыми запущен скрипт

Обратите внимание на то, что параметры командной строки разделяются пробелами.

Взглянем на ещё один пример использования параметров. Тут мы найдём сумму чисел, переданных сценарию:

#!/bin/bash total=$[ $1 + $2 ] echo The first parameter is $1. echo The second parameter is $2. echo The sum is $total.
Запустим скрипт и проверим результат вычислений.


Сценарий, который находит сумму переданных ему чисел

Параметры командной строки не обязательно должны быть числами. Сценариям можно передавать и строки. Например, вот скрипт, работающий со строкой:

#!/bin/bash echo Hello $1, how do you do
Запустим его:

./myscript Adam
Он выведет то, что мы от него ожидаем.


Сценарий, работающий со строковым параметром

Что если параметр содержит пробелы, а нам надо обрабатывать его как самостоятельный фрагмент данных? Полагаем, если вы освоили предыдущие части этого руководства, ответ вы уже знаете. Заключается он в использовании кавычек.

Если скрипту надо больше девяти параметров, при обращении к ним номер в имени переменной надо заключать в фигурные скобки, например так:

Проверка параметров

Если скрипт вызван без параметров, но для нормальной работы кода предполагается их наличие, возникнет ошибка. Поэтому рекомендуется всегда проверять наличие параметров, переданных сценарию при вызове. Например, это можно организовать так:

#!/bin/bash if [ -n "$1" ] then echo Hello $1. else echo "No parameters found. " fi
Вызовем скрипт сначала с параметром, а потом без параметров.


Вызов скрипта, проверяющего наличие параметров командной строки

Подсчёт параметров

В скрипте можно подсчитать количество переданных ему параметров. Оболочка bash предоставляет для этого специальную переменную. А именно, переменная $# содержит количество параметров, переданных сценарию при вызове.

Опробуем её:

#!/bin/bash echo There were $# parameters passed.
Вызовем сценарий.

./myscript 1 2 3 4 5
В результате скрипт сообщит о том, что ему передано 5 параметров.


Подсчёт количества параметров в скрипте

Эта переменная даёт необычный способ получения последнего из переданных скрипту параметров, не требующий знания их количества. Вот как это выглядит:

#!/bin/bash echo The last parameter was ${!#}
Вызовем скрипт и посмотрим, что он выведет.


Обращение к последнему параметру

Захват всех параметров командной строки

В некоторых случаях нужно захватить все параметры, переданные скрипту. Для этого можно воспользоваться переменными $* и $@ . Обе они содержат все параметры командной строки, что делает возможным доступ к тому, что передано сценарию, без использования позиционных параметров.

Переменная $* содержит все параметры, введённые в командной строке, в виде единого «слова».

В переменной $@ параметры разбиты на отдельные «слова». Эти параметры можно перебирать в циклах.

Рассмотрим разницу между этими переменными на примерах. Сначала взглянем на их содержимое:

#!/bin/bash echo "Using the \$* method: $*" echo "-----------" echo "Using the \$@ method: $@"
Вот вывод скрипта.


Переменные $* и $@

Как видно, при выводе обеих переменных получается одно и то же. Теперь попробуем пройтись по содержимому этих переменных в циклах для того, чтобы увидеть разницу между ними:

#!/bin/bash count=1 for param in "$*" do echo "\$* Parameter #$count = $param" count=$(($count + 1)) done count=1 for param in "$@" do echo "\$@ Parameter #$count = $param" count=$(($count + 1)) done
Взгляните на то, что скрипт вывел в консоль. Разница между переменными вполне очевидна.


Разбор переменных $* и $@ в цикле

Переменная $* содержит все переданные скрипту параметры как единый фрагмент данных, в то время как в переменной $@ они представлены самостоятельными значениями. Какой именно переменной воспользоваться - зависит от того, что именно нужно в конкретном сценарии.

Команда shift

Использовать команду shift в bash-скриптах следует с осторожностью, так как она, в прямом смысле слова, сдвигает значения позиционных параметров.

Когда вы используете эту команду, она, по умолчанию, сдвигает значения позиционных параметров влево. Например, значение переменной $3 становится значением переменной $2 , значение $2 переходит в $1 , а то, что было до этого в $1, теряется. Обратите внимание на то, что при этом значение переменной $0 , содержащей имя скрипта, не меняется.

Воспользовавшись командой shift , рассмотрим ещё один способ перебора переданных скрипту параметров:

#!/bin/bash count=1 while [ -n "$1" ] do echo "Parameter #$count = $1" count=$(($count + 1)) shift done
Скрипт задействует цикл while , проверяя длину значения первого параметра. Когда длина станет равна нулю, происходит выход из цикла. После проверки первого параметра и вывода его на экран, вызывается команда shift , которая сдвигает значения параметров на одну позицию.


Использование команды shift для перебора параметров

Используя команду shift , помните о том, что при каждом её вызове значение переменной $1 безвозвратно теряется.

Ключи командной строки

Ключи командной строки обычно выглядят как буквы, перед которыми ставится тире. Они служат для управления сценариями. Рассмотрим такой пример:

#!/bin/bash echo while [ -n "$1" ] do case "$1" in -a) echo "Found the -a option" ;; -b) echo "Found the -b option" ;; -c) echo "Found the -c option" ;; *) echo "$1 is not an option" ;; esac shift done
Запустим скрипт:

$ ./myscript –a –b –c –d
И проанализируем то, что он выведет в терминал.


Обработка ключей в скрипте

В этом коде использована конструкция case , которая сверяет переданный ей ключ со списком обрабатываемых скриптом ключей. Если переданное значение нашлось в этом списке, выполняется соответствующая ветвь кода. Если при вызове скрипта будет использован любой ключ, обработка которого не предусмотрена, будет исполнена ветвь «*».

Как различать ключи и параметры

Часто при написании bash-скриптов возникает ситуация, когда надо использовать и параметры командной строки, и ключи. Стандартный способ это сделать заключается в применении специальной последовательности символов, которая сообщает скрипту о том, когда заканчиваются ключи и начинаются обычные параметры.

Эта последовательность - двойное тире (--). Оболочка использует её для указания позиции, на которой заканчивается список ключей. После того, как скрипт обнаружит признак окончания ключей, то, что осталось, можно, не опасаясь ошибок, обрабатывать как параметры, а не как ключи. Рассмотрим пример:

#!/bin/bash while [ -n "$1" ] do case "$1" in -a) echo "Found the -a option" ;; -b) echo "Found the -b option";; -c) echo "Found the -c option" ;; --) shift break ;; *) echo "$1 is not an option";; esac shift done count=1 for param in $@ do echo "Parameter #$count: $param" count=$(($count + 1)) done
Этот сценарий использует команду break для прерывания цикла while при обнаружении в строке двойного тире.

Вот что получится после его вызова.


Обработка ключей и параметров командной строки

Как видно, когда скрипт, разбирая переданные ему данные, находит двойное тире, он завершает обработку ключей и считает всё, что ещё не обработано, параметрами.

Обработка ключей со значениями

По мере усложнения ваших скриптов, вы столкнётесь с ситуациями, когда обычных ключей уже недостаточно, а значит, нужно будет использовать ключи с некими значениями. Например, вызов сценария в котором используется подобная возможность, выглядит так:

./myscript -a test1 -b -c test2
Скрипт должен уметь определять, когда вместе с ключами командной строки используются дополнительные параметры:

#!/bin/bash while [ -n "$1" ] do case "$1" in -a) echo "Found the -a option";; -b) param="$2" echo "Found the -b option, with parameter value $param" shift ;; -c) echo "Found the -c option";; --) shift break ;; *) echo "$1 is not an option";; esac shift done count=1 for param in "$@" do echo "Parameter #$count: $param" count=$(($count + 1)) done
Вызовем этот скрипт в таком виде:

./myscript -a -b test1 -d
Посмотрим на результаты его работы.


Обработка параметров ключей

В данном примере в конструкции case обрабатываются три ключа. Ключ -b требует наличия дополнительного параметра. Так как обрабатываемый ключ находится в переменной $1 , соответствующий ему параметр будет находиться в $2 (тут используется команда shift , поэтому, по мере обработки, всё, что передано сценарию, сдвигается влево). Когда с этим мы разобрались, осталось лишь извлечь значение переменной $2 и у нас будет параметр нужного ключа. Конечно, тут понадобится ещё одна команда shift для того, чтобы следующий ключ попал в $1 .

Использование стандартных ключей

При написании bash-скриптов вы можете выбирать любые буквы для ключей командной строки и произвольно задавать реакцию скрипта на эти ключи. Однако, в мире Linux значения некоторых ключей стали чем-то вроде стандарта, которого полезно придерживаться. Вот список этих ключей:
-a Вывести все объекты.
-c Произвести подсчёт.
-d Указать директорию.
-e Развернуть объект.
-f Указать файл, из которого нужно прочитать данные.
-h Вывести справку по команде.
-i Игнорировать регистр символов.
-l Выполнить полноформатный вывод данных.
-n Использовать неинтерактивный (пакетный) режим.
-o Позволяет указать файл, в который нужно перенаправить вывод.
-q Выполнить скрипт в quiet-режиме.
-r Обрабатывать папки и файлы рекурсивно.
-s Выполнить скрипт в silent-режиме.
-v Выполнить многословный вывод.
-x Исключить объект.
-y Ответить «yes» на все вопросы.

Если вы работаете в Linux, вам, скорее всего, знакомы многие из этих ключей. Использовав их в общепринятом значении в своих скриптах, вы поможете пользователям взаимодействовать с ними, не беспокоясь о чтении документации.

Получение данных от пользователя

Ключи и параметры командной строки - это отличный способ получить данные от того, кто пользуется скриптом, однако в некоторых случаях нужно больше интерактивности.

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

Эта команда позволяет принимать введённые данные либо со стандартного ввода (с клавиатуры), либо используя другие дескрипторы файлов. После получения данных, эта команда помещает их в переменную:

#!/bin/bash echo -n "Enter your name: " read name echo "Hello $name, welcome to my program."
Обратите внимание на то, что команда echo , которая выводит приглашение, вызывается с ключом -n . Это приводит к тому, что в конце приглашения не выводится знак перевода строки, что позволяет пользователю скрипта вводить данные там же, где расположено приглашение, а не на следующей строке.


Обработка пользовательского ввода

При вызове read можно указывать и несколько переменных:

#!/bin/bash read -p "Enter your name: " first last echo "Your data for $last, $first…"
Вот что выведет скрипт после запуска.


Несколько переменных в команде read

Если, вызвав read , не указывать переменную, данные, введённые пользователем, будут помещены в специальную переменную среды REPLY:

#!/bin/bash read -p "Enter your name: " echo Hello $REPLY, welcome to my program.


Использование переменной среды REPLY

Если скрипт должен продолжать выполнение независимо от того, введёт пользователь какие-то данные или нет, вызывая команду read можно воспользоваться ключом -t . А именно, параметр ключа задаёт время ожидания ввода в секундах:

#!/bin/bash if read -t 5 -p "Enter your name: " name then echo "Hello $name, welcome to my script" else echo "Sorry, too slow! " fi
Если данные не будут введены в течение 5 секунд, скрипт выполнит ветвь условного оператора else , выведя извинения.


Ограничение времени на ввод данных

Ввод паролей

Иногда то, что вводит пользователь в ответ на вопрос скрипта, лучше на экране не показывать. Например, так обычно делают, запрашивая пароли. Ключ -s команды read предотвращает отображение на экране данных, вводимых с клавиатуры. На самом деле, данные выводятся, но команда read делает цвет текста таким же, как цвет фона.

#!/bin/bash read -s -p "Enter your password: " pass echo "Is your password really $pass? "
Вот как отработает этот скрипт.


Ввод конфиденциальных данных

Чтение данных из файла

Команда read может, при каждом вызове, читать одну строку текста из файла. Когда в файле больше не останется непрочитанных строк, она просто остановится. Если нужно получить в скрипте всё содержимое файла, можно, с помощью конвейера, передать результаты вызова команды cat для файла, конструкции while , которая содержит команду read (конечно, использование команды cat выглядит примитивно, но наша цель - показать всё максимально просто, ориентируясь на новичков; опытные пользователи, уверены, это поймут).

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

#!/bin/bash count=1 cat myfile | while read line do echo "Line $count: $line" count=$(($count + 1)) done echo "Finished"
Посмотрим на него в деле.


Чтение данных из файла

Тут мы передали в цикл while содержимое файла и перебрали все строки этого файла, выводя номер и содержимое каждой из них.

Итоги

Сегодня мы разобрали работу с ключами и параметрами командной строки. Без этих средств диапазон использования скриптов оказывается чрезвычайно узким. Даже если скрипт написан, что называется, «для себя». Тут же мы рассмотрели подходы к получению данных от пользователя во время выполнения программы - это делает сценарии интерактивными.

В следующий раз поговорим об операциях ввода и вывода.

Уважаемые читатели! Спасибо вам за то, что делитесь опытом в комментариях к предыдущим частям этого цикла материалов. Если вам есть что сказать об обработке всего того, что можно передать в скрипт при запуске или во время его работы, уверены, многим будет интересно об этом почитать.

Освоив предыдущие части этой серии материалов, вы узнали о том, что такое bash-скрипты, как их писать, как управлять потоком выполнения программы, как работать с файлами. Сегодня мы поговорим о том, как добавить скриптам интерактивности, оснастив их возможностями по получению данных от пользователя и по обработке этих данных.

Наиболее распространённый способ передачи данных сценариям заключается в использовании параметров командной строки. Вызвав сценарий с параметрами, мы передаём ему некую информацию, с которой он может работать. Выглядит это так:

$ ./myscript 10 20
В данном примере сценарию передано два параметра - «10» и «20». Всё это хорошо, но как прочесть данные в скрипте?

Чтение параметров командной строки

Оболочка bash назначает специальным переменным, называемым позиционными параметрами, введённые при вызове скрипта параметры командной строки:
  • $0 - имя скрипта.
  • $1 - первый параметр.
  • $2 - второй параметр - и так далее, вплоть до переменной $9 , в которую попадает девятый параметр.
Вот как можно использовать параметры командной строки в скрипте с помощью этих переменных:

#!/bin/bash echo $0 echo $1 echo $2 echo $3
Запустим сценарий с параметрами:

./myscript 5 10 15
Вот что он выведет в консоль.


Вывод параметров, с которыми запущен скрипт

Обратите внимание на то, что параметры командной строки разделяются пробелами.

Взглянем на ещё один пример использования параметров. Тут мы найдём сумму чисел, переданных сценарию:

#!/bin/bash total=$[ $1 + $2 ] echo The first parameter is $1. echo The second parameter is $2. echo The sum is $total.
Запустим скрипт и проверим результат вычислений.


Сценарий, который находит сумму переданных ему чисел

Параметры командной строки не обязательно должны быть числами. Сценариям можно передавать и строки. Например, вот скрипт, работающий со строкой:

#!/bin/bash echo Hello $1, how do you do
Запустим его:

./myscript Adam
Он выведет то, что мы от него ожидаем.


Сценарий, работающий со строковым параметром

Что если параметр содержит пробелы, а нам надо обрабатывать его как самостоятельный фрагмент данных? Полагаем, если вы освоили предыдущие части этого руководства, ответ вы уже знаете. Заключается он в использовании кавычек.

Если скрипту надо больше девяти параметров, при обращении к ним номер в имени переменной надо заключать в фигурные скобки, например так:

Проверка параметров

Если скрипт вызван без параметров, но для нормальной работы кода предполагается их наличие, возникнет ошибка. Поэтому рекомендуется всегда проверять наличие параметров, переданных сценарию при вызове. Например, это можно организовать так:

#!/bin/bash if [ -n "$1" ] then echo Hello $1. else echo "No parameters found. " fi
Вызовем скрипт сначала с параметром, а потом без параметров.


Вызов скрипта, проверяющего наличие параметров командной строки

Подсчёт параметров

В скрипте можно подсчитать количество переданных ему параметров. Оболочка bash предоставляет для этого специальную переменную. А именно, переменная $# содержит количество параметров, переданных сценарию при вызове.

Опробуем её:

#!/bin/bash echo There were $# parameters passed.
Вызовем сценарий.

./myscript 1 2 3 4 5
В результате скрипт сообщит о том, что ему передано 5 параметров.


Подсчёт количества параметров в скрипте

Эта переменная даёт необычный способ получения последнего из переданных скрипту параметров, не требующий знания их количества. Вот как это выглядит:

#!/bin/bash echo The last parameter was ${!#}
Вызовем скрипт и посмотрим, что он выведет.


Обращение к последнему параметру

Захват всех параметров командной строки

В некоторых случаях нужно захватить все параметры, переданные скрипту. Для этого можно воспользоваться переменными $* и $@ . Обе они содержат все параметры командной строки, что делает возможным доступ к тому, что передано сценарию, без использования позиционных параметров.

Переменная $* содержит все параметры, введённые в командной строке, в виде единого «слова».

В переменной $@ параметры разбиты на отдельные «слова». Эти параметры можно перебирать в циклах.

Рассмотрим разницу между этими переменными на примерах. Сначала взглянем на их содержимое:

#!/bin/bash echo "Using the \$* method: $*" echo "-----------" echo "Using the \$@ method: $@"
Вот вывод скрипта.


Переменные $* и $@

Как видно, при выводе обеих переменных получается одно и то же. Теперь попробуем пройтись по содержимому этих переменных в циклах для того, чтобы увидеть разницу между ними:

#!/bin/bash count=1 for param in "$*" do echo "\$* Parameter #$count = $param" count=$(($count + 1)) done count=1 for param in "$@" do echo "\$@ Parameter #$count = $param" count=$(($count + 1)) done
Взгляните на то, что скрипт вывел в консоль. Разница между переменными вполне очевидна.


Разбор переменных $* и $@ в цикле

Переменная $* содержит все переданные скрипту параметры как единый фрагмент данных, в то время как в переменной $@ они представлены самостоятельными значениями. Какой именно переменной воспользоваться - зависит от того, что именно нужно в конкретном сценарии.

Команда shift

Использовать команду shift в bash-скриптах следует с осторожностью, так как она, в прямом смысле слова, сдвигает значения позиционных параметров.

Когда вы используете эту команду, она, по умолчанию, сдвигает значения позиционных параметров влево. Например, значение переменной $3 становится значением переменной $2 , значение $2 переходит в $1 , а то, что было до этого в $1, теряется. Обратите внимание на то, что при этом значение переменной $0 , содержащей имя скрипта, не меняется.

Воспользовавшись командой shift , рассмотрим ещё один способ перебора переданных скрипту параметров:

#!/bin/bash count=1 while [ -n "$1" ] do echo "Parameter #$count = $1" count=$(($count + 1)) shift done
Скрипт задействует цикл while , проверяя длину значения первого параметра. Когда длина станет равна нулю, происходит выход из цикла. После проверки первого параметра и вывода его на экран, вызывается команда shift , которая сдвигает значения параметров на одну позицию.


Использование команды shift для перебора параметров

Используя команду shift , помните о том, что при каждом её вызове значение переменной $1 безвозвратно теряется.

Ключи командной строки

Ключи командной строки обычно выглядят как буквы, перед которыми ставится тире. Они служат для управления сценариями. Рассмотрим такой пример:

#!/bin/bash echo while [ -n "$1" ] do case "$1" in -a) echo "Found the -a option" ;; -b) echo "Found the -b option" ;; -c) echo "Found the -c option" ;; *) echo "$1 is not an option" ;; esac shift done
Запустим скрипт:

$ ./myscript –a –b –c –d
И проанализируем то, что он выведет в терминал.


Обработка ключей в скрипте

В этом коде использована конструкция case , которая сверяет переданный ей ключ со списком обрабатываемых скриптом ключей. Если переданное значение нашлось в этом списке, выполняется соответствующая ветвь кода. Если при вызове скрипта будет использован любой ключ, обработка которого не предусмотрена, будет исполнена ветвь «*».

Как различать ключи и параметры

Часто при написании bash-скриптов возникает ситуация, когда надо использовать и параметры командной строки, и ключи. Стандартный способ это сделать заключается в применении специальной последовательности символов, которая сообщает скрипту о том, когда заканчиваются ключи и начинаются обычные параметры.

Эта последовательность - двойное тире (--). Оболочка использует её для указания позиции, на которой заканчивается список ключей. После того, как скрипт обнаружит признак окончания ключей, то, что осталось, можно, не опасаясь ошибок, обрабатывать как параметры, а не как ключи. Рассмотрим пример:

#!/bin/bash while [ -n "$1" ] do case "$1" in -a) echo "Found the -a option" ;; -b) echo "Found the -b option";; -c) echo "Found the -c option" ;; --) shift break ;; *) echo "$1 is not an option";; esac shift done count=1 for param in $@ do echo "Parameter #$count: $param" count=$(($count + 1)) done
Этот сценарий использует команду break для прерывания цикла while при обнаружении в строке двойного тире.

Вот что получится после его вызова.


Обработка ключей и параметров командной строки

Как видно, когда скрипт, разбирая переданные ему данные, находит двойное тире, он завершает обработку ключей и считает всё, что ещё не обработано, параметрами.

Обработка ключей со значениями

По мере усложнения ваших скриптов, вы столкнётесь с ситуациями, когда обычных ключей уже недостаточно, а значит, нужно будет использовать ключи с некими значениями. Например, вызов сценария в котором используется подобная возможность, выглядит так:

./myscript -a test1 -b -c test2
Скрипт должен уметь определять, когда вместе с ключами командной строки используются дополнительные параметры:

#!/bin/bash while [ -n "$1" ] do case "$1" in -a) echo "Found the -a option";; -b) param="$2" echo "Found the -b option, with parameter value $param" shift ;; -c) echo "Found the -c option";; --) shift break ;; *) echo "$1 is not an option";; esac shift done count=1 for param in "$@" do echo "Parameter #$count: $param" count=$(($count + 1)) done
Вызовем этот скрипт в таком виде:

./myscript -a -b test1 -d
Посмотрим на результаты его работы.


Обработка параметров ключей

В данном примере в конструкции case обрабатываются три ключа. Ключ -b требует наличия дополнительного параметра. Так как обрабатываемый ключ находится в переменной $1 , соответствующий ему параметр будет находиться в $2 (тут используется команда shift , поэтому, по мере обработки, всё, что передано сценарию, сдвигается влево). Когда с этим мы разобрались, осталось лишь извлечь значение переменной $2 и у нас будет параметр нужного ключа. Конечно, тут понадобится ещё одна команда shift для того, чтобы следующий ключ попал в $1 .

Использование стандартных ключей

При написании bash-скриптов вы можете выбирать любые буквы для ключей командной строки и произвольно задавать реакцию скрипта на эти ключи. Однако, в мире Linux значения некоторых ключей стали чем-то вроде стандарта, которого полезно придерживаться. Вот список этих ключей:
-a Вывести все объекты.
-c Произвести подсчёт.
-d Указать директорию.
-e Развернуть объект.
-f Указать файл, из которого нужно прочитать данные.
-h Вывести справку по команде.
-i Игнорировать регистр символов.
-l Выполнить полноформатный вывод данных.
-n Использовать неинтерактивный (пакетный) режим.
-o Позволяет указать файл, в который нужно перенаправить вывод.
-q Выполнить скрипт в quiet-режиме.
-r Обрабатывать папки и файлы рекурсивно.
-s Выполнить скрипт в silent-режиме.
-v Выполнить многословный вывод.
-x Исключить объект.
-y Ответить «yes» на все вопросы.

Если вы работаете в Linux, вам, скорее всего, знакомы многие из этих ключей. Использовав их в общепринятом значении в своих скриптах, вы поможете пользователям взаимодействовать с ними, не беспокоясь о чтении документации.

Получение данных от пользователя

Ключи и параметры командной строки - это отличный способ получить данные от того, кто пользуется скриптом, однако в некоторых случаях нужно больше интерактивности.

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

Эта команда позволяет принимать введённые данные либо со стандартного ввода (с клавиатуры), либо используя другие дескрипторы файлов. После получения данных, эта команда помещает их в переменную:

#!/bin/bash echo -n "Enter your name: " read name echo "Hello $name, welcome to my program."
Обратите внимание на то, что команда echo , которая выводит приглашение, вызывается с ключом -n . Это приводит к тому, что в конце приглашения не выводится знак перевода строки, что позволяет пользователю скрипта вводить данные там же, где расположено приглашение, а не на следующей строке.


Обработка пользовательского ввода

При вызове read можно указывать и несколько переменных:

#!/bin/bash read -p "Enter your name: " first last echo "Your data for $last, $first…"
Вот что выведет скрипт после запуска.


Несколько переменных в команде read

Если, вызвав read , не указывать переменную, данные, введённые пользователем, будут помещены в специальную переменную среды REPLY:

#!/bin/bash read -p "Enter your name: " echo Hello $REPLY, welcome to my program.


Использование переменной среды REPLY

Если скрипт должен продолжать выполнение независимо от того, введёт пользователь какие-то данные или нет, вызывая команду read можно воспользоваться ключом -t . А именно, параметр ключа задаёт время ожидания ввода в секундах:

#!/bin/bash if read -t 5 -p "Enter your name: " name then echo "Hello $name, welcome to my script" else echo "Sorry, too slow! " fi
Если данные не будут введены в течение 5 секунд, скрипт выполнит ветвь условного оператора else , выведя извинения.


Ограничение времени на ввод данных

Ввод паролей

Иногда то, что вводит пользователь в ответ на вопрос скрипта, лучше на экране не показывать. Например, так обычно делают, запрашивая пароли. Ключ -s команды read предотвращает отображение на экране данных, вводимых с клавиатуры. На самом деле, данные выводятся, но команда read делает цвет текста таким же, как цвет фона.

#!/bin/bash read -s -p "Enter your password: " pass echo "Is your password really $pass? "
Вот как отработает этот скрипт.


Ввод конфиденциальных данных

Чтение данных из файла

Команда read может, при каждом вызове, читать одну строку текста из файла. Когда в файле больше не останется непрочитанных строк, она просто остановится. Если нужно получить в скрипте всё содержимое файла, можно, с помощью конвейера, передать результаты вызова команды cat для файла, конструкции while , которая содержит команду read (конечно, использование команды cat выглядит примитивно, но наша цель - показать всё максимально просто, ориентируясь на новичков; опытные пользователи, уверены, это поймут).

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

#!/bin/bash count=1 cat myfile | while read line do echo "Line $count: $line" count=$(($count + 1)) done echo "Finished"
Посмотрим на него в деле.


Чтение данных из файла

Тут мы передали в цикл while содержимое файла и перебрали все строки этого файла, выводя номер и содержимое каждой из них.

Итоги

Сегодня мы разобрали работу с ключами и параметрами командной строки. Без этих средств диапазон использования скриптов оказывается чрезвычайно узким. Даже если скрипт написан, что называется, «для себя». Тут же мы рассмотрели подходы к получению данных от пользователя во время выполнения программы - это делает сценарии интерактивными.

В следующий раз поговорим об операциях ввода и вывода.

Уважаемые читатели! Спасибо вам за то, что делитесь опытом в комментариях к предыдущим частям этого цикла материалов. Если вам есть что сказать об обработке всего того, что можно передать в скрипт при запуске или во время его работы, уверены, многим будет интересно об этом почитать.

Я создал скрипт со стандартной магией getopt , поэтому вы можете вызвать скрипт с помощью

Batman-connect -c ffki

Как добавить параметр для вызова этого скрипта только с одним параметром без тире?

batman-connect ffki

поэтому он будет интерпретировать этот единственный вариант как второй аргумент для -c ?

В соответствии с этим ответом я попытался:

If [ $@ = "ffki" ]; then set -- "-c $@" fi

Но это приводит к ошибке, я думаю, потому что эта строка в моем скрипте изменит ее:

# Execute getopt on the arguments passed to this program, identified by the special character $@ PARSED_OPTIONS=$(getopt -n "$0" -o hsrvVi:c: --long "help,start,restart,stop,verbose,vv,version,interface:,community:" -- "$@")

Мне не нравится переустанавливать весь мой скрипт, так что есть простой однострочный набор, чтобы просто установить первый аргумент «-c», а второй – «ffki»?

4 Solutions collect form web for “Измените аргументы bash, если задан только один аргумент”

Вы можете сделать что-то вроде

If [ $# = 1 ]; then # do whatever you"d do when there is only one argument else # do the getopt bits fi # do what you"d do in either case

Если -c – единственный коммутатор, который вы хотите поддержать, то вам не нужен getopt и вместо этого можно сделать что-то вроде этого:

#!/bin/bash usage() { echo "Usage: $0 [ -c ] arg" >&2 exit 1 } if [ $# = 2 ]; then if [ $1 = "-c" ]; then shift c_switch_value="$1" else usage fi elif [ $# = 1 ]; then c_switch_value="$1" else usage fi

Является ли это более удобным для чтения является спорным, хотя.

If [ $# = 1 ]; then # If there is only one argument, replace the current process with the new invocation of the script # the only option will be used as -c option exec "$0" -c "$@" fi

Это не отвечает на начальный вопрос, но это обходной путь, который работает в моем специальном случае.

Команда в предложении if расширяется до [ за которым следует список расширений подстановок слов, которые составляют позиционные параметры followeb словами = ffki ] . Вообще говоря, $VAR не означает «значение VAR », когда оно находится вне двойных кавычек, это означает «разделить значение VAR на отдельные слова и интерпретировать их как глобусы». См. Почему мой сценарий оболочки задыхается от пробелов или других специальных символов?

Кроме того, "$@" – это особый случай: он расширяется до списка позиционных параметров (без дальнейшего расширения), а не до одного слова (несмотря на кавычки). Вы можете использовать "$*" , который расширяется до одного слова, состоящего из позиционных параметров, разделенных пробелом (точнее, первым символом значения IFS). Поэтому, чтобы проверить, есть ли один позиционный параметр, значение которого ffki , вы можете использовать

If [ "$*" = "ffki" ]; then

If [ $# -eq 1 ] && [ "$1" = "ffki" ]; then

Чтобы изменить параметры позиционирования, вы были близки: используйте встроенный set и передайте новый набор позиционных параметров в виде отдельных аргументов. Если существует риск того, что первый параметр начинается с a - , используйте set -- … так, чтобы он не интерпретировался как опция.

If [ "$*" = "ffki" ]; then set -- -c "ffki" fi



Понравилась статья? Поделиться с друзьями: