Меню Закрыть

Аргументы командной строки bash

Содержание

Функциональность интерпретатора Bash позволяет работать не только со статистическими данными, записанными в скриптах. Иногда возникает необходимость добавить сценарию интерактивности, позволяя принимать внешние параметры скрипта для манипуляции ими в коде.

В этой статье будет рассмотрено, как принимать аргументы командной строки bash, способы его обработки, проверка опций, а также известные особенности при работе с ними.

Параметры скрипта Bash

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

  • $0 — название сценария;
  • $1 — первый параметр;
  • .
  • $9 — девятый параметр сценария.

Ниже приведён пример использования одного параметра скрипта Bash:

#!/bin/bash
factorial=1
for (( number = 1; number

Результат работы кода:

Переменная $1 может использоваться в коде точно так же, как и любая другая. Скрипт автоматически присваивает ей значение из параметра командой строки — пользователю не нужно делать это вручную.

Если необходимо ввести дополнительные параметры, их следует разделить в командной строке пробелами.

#!/bin/bash
total=$[ $1 * $2 ]
echo "Первый параметр равен $1."
echo "Второй параметр равен $2."
echo "Произведение параметров равно $total."

Пример работы кода:

Командный интерпретатор присвоил числа 5 и 10 соответствующим переменным — $1 и $2.

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

#!/bin/bash
echo "Добро пожаловать, $1"

Пример работы кода:

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

Если необходимо использовать больше 9 параметров для скрипта, то названия переменных немного изменятся. Начиная с десятой переменной, число, стоящее после знака $, необходимо заключать в квадратные скобки (без внутренних пробелов):

#!/bin/bash
total=$[ $ <10>* $ <11>]
echo "Десятый параметр равен $<10>"
echo "Одиннадцатый параметр равен $<11>"
echo "Произведение этих параметров равно $total"

Пример работы кода:

Получение названия скрипта Bash

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

#!/bin/bash
echo "Имя сценария: $0"

Пример работы кода:

Как видно, если строкой, фактически переданной в переменную $0, является весь путь к сценарию, то на вывод будет идти весь путь, а не только название программы.

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

Для этого специально предусмотрена небольшая команда. Команда basename возвращает только название скрипта без абсолютного или относительного пути к нему:

#!/bin/bash
name=`basename $0`
echo "Имя запущенной программы: $name"

Результат работы кода:

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

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

Если попробовать запустить написанный ранее скрипт test2 без аргументов, то перед выводом команд echo будет отображена ошибка:

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

#!/bin/bash
if [ -n "$1" ]
then
echo "Добро пожаловать, $1"
else
echo "Простите, вы не представились"
fi

Пример работы кода:

В данном случае использовалась опция -n из предыдущей статьи о сравнении строк в Bash для проверки на наличие значения в переменной, которая считала параметр.

Обработка неизветсного числа параметров

Для начала рассмотрим один из часто используемых инструментов при работе с параметрами Bash — команду shift. Её прямое назначение заключается в сдвиге параметров на одну позицию влево. Таким образом, значение из переменной $3 переместится в $2, а из $2 — в $1. Но из $1 значение просто отбросится и не сместится в $0, так как там неизменно хранится название запущенной программы.

Эта команда является эффективным способом обработки всех параметров, переданных сценарию, особенно, когда нельзя заранее узнать их количество. Достаточно лишь обработать $1, сделать сдвиг и повторить процедуру.

#!/bin/bash
count=1
while [ -n "$1" ]
do
echo "Параметр №$count = $1"
count=$[ $count + 1 ]
shift
done

Пример работы кода:

Этот скрипт выполняет цикл while, в условии которого указана проверка первого параметра на длину. И если она равна нулю, цикл прерывает свою работу. При положительном результате проверки команда shift сдвигает все параметры влево на одну позицию.

Ещё один вариант использование shift — смещать на несколько позиций. Для этого достаточно через пробел указать количество, на которое будет смещён ряд параметров скрипта.

#!/bin/bash
echo "Первый параметр из переданных: $1"
shift 2
echo "Теперь первый параметр: $1"

Пример работы скрипта:

На заметку: при использовании shift нужно быть осторожным, ведь сдвинутые за пределы $1 параметры не восстанавливаются в период работы программы.

Обработка опций в Bash

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

По примеру выше можно применять shift для обработки простых опций. С помощью инструкции case можно определять, являются ли аргументы Bash опциями:

#!/bin/bash
while [ -n "$1" ]
do
case "$1" in
-a | -b | -c) echo "Найдена опция $1" ;;
*) echo "$1 не опция" ;;
esac
shift
done

Пример работы программы:

Блок case работает правильно вне зависимости от того, как расположены аргументы командной строки bash.

Выводы

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

Команда basename используется для обрезания пути запущенного сценария, что часто необходимо для создания гибких программ. Использование команды shift позволяет эффективно проходить по переданным скрипту параметрам, особенно когда их количество неизвестно.

Еще больше основ программирования в bash

Обработка аргументов

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

В простом скрипте из предыдущей статьи мы использовали переменную «$1«, которая содержит первый аргумент командной строки при вызове скрипта. Аналогично можно использовать «$2», «$3» и так далее для доступа ко второму, третьему… аргументам командной строки. Вот пример:

#!/bin/bash echo "Имя скрипта — $0" echo "Первый аргумент: $1" echo "Второй аргумент: $<2>" echo "Семнадцатый аргумент: $<17>" echo "Количество аргументов: $#"

Обратите внимание, что в переменной «$0» содержится имя самого скрипта, который запущен из командной строки. А переменная «$#» содержит количество переданных скрипту аргументов. Использование фигурных скобок необязательно только для переменных состоящих из одной цифры (с $0 по $9). Попробуйте позапускать этот скрипт с разным числом аргументов и посмотрите как он работает.

Иногда необходимо сослаться сразу на все аргументы командной строки. Для этого в bash есть специальная переменная «$@«, которая содержит все аргументы переданные скрипту разделенные пробелами. Мы будем использовать эту переменную чуть позже при рассказе о циклах со счетчиком (конструкция «for»).

Управляющие конструкции bash

Если вы раньше программировали на процедурных языках, таких как Си, Паскаль, Перл и тому подобных, вам должны быть знакомы управляющие конструкции вроде «if», «for» и другие. В bash тоже есть все эти конструкции. В следующих разделах пособия я познакомлю вас с ними и покажу чем они отличаются от подобных конструкций из других языков программирования. Если вы раньше не программировали — не волнуйтесь. Материал будет изложен подробно и дополнен примерами, так что даже новичок в программировании сможет разобраться.

Оператор условного выбора «if»

Если вы раньше программировали на языке Си, то должны знать сколько требуется усилий чтобы определить какой из двух файлов был создан первым, например. А все из-за того, что в Си нет встроенных средств для такого рода сравнения. Вместо этого приходится использовать системный вызов stat() для каждого файла и затем сравнивать результат вручную. Но в bash есть встроенный механизм сравнения файлов, Поэтому узнать «доступен ли для чтения файл /tmp/myfile» настолько же просто как и узнать «превосходит ли значение переменной ‘myvar’ 4».

Читайте также:  File station synology windows

Привожу список наиболее часто употребляемых в bash операторов сравнения

Файлы -a file истинно если файл существует. -d file истинно если файл существует и является директорией. -f file истинно если файл существует и является обычным файлом. -r file истинно если файл существует и доступен для чтения. -s file истинно если файл существует и его размер больше 0. -w file истинно если файл существует и доступен для записи. -x file истинно если файл существует и является исполняемым. file1 -nt file2 истинно если файл file1 новее чем file2 или file1 (в соответствии со временем последнего изменения) существует, а file2 нет. file1 -ot file2 истинно если файл file1 старше чем file2 или file2 существует, а file1 нет. file1 -ef file2 истинно если оба файла ссылаются на одно и то же устройство или инод. Строки -z string истинно если строка имеет нулевую длину. -n string истинно если длина строки не нулевая. string1 = string2 истинно если строки равны. string1 != string2 истинно если не равны. string1 истинно если строка 1 стоит в алфавитном порядке перед строкой 2. string1 > string2 истинно если строка 1 стоит в алфавитном порядке после строки 2.

В следующих примерах показано как использовать оператор сравнения в конструкции «if»:

if [ -z "$myvar" ] then echo "Переменная ‘myvar’ не определена." fi

Квадратные скобки вычисляют условное выражение стоящее в них (это синоним встроенной функции bash — test). Возвращаемый результат — 1 или 0 в зависимости от того выполняется условие или нет. в скобках может стоять несколько выражений, связанных логическими операторами «и» или «или«. Подробнее на странице справки help test.

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

if [ "$myvar" -eq 3 ] then echo "myvar равно 3" fi if [ "$myvar" = "3" ] then echo "myvar равно 3" fi

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

Тонкости при сравнении строк

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

if [ $myvar = "foo bar oni" ] then echo "yes" fi

В этом примере, если значение переменной «$myvar» будет равно «foo», код будет работать как и ожидается и не печатать ничего. Но если значение переменной «$myvar» будет равно «foo bar oni», скрипт вызовет следующую ошибку:

[: too many arguments

После подстановки значения переменной, bash пытается произвести следующую операцию сравнения:

[ foo bar oni = "foo bar oni" ]

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

if [ "$myvar" = "foo bar oni" ] then echo "yes" fi

Этот код будет работать корректно и не преподнесет нам больше никаких неприятных сюрпризов.

Конструкция создания циклов «for»

Хорошо, с условными переходами разобрались, пора перейти к циклическим конструкциям. Начнем с управляющей конструкции «for«. Вот стандартный пример:

#!/bin/bash for x in one two three four do echo "number $x" done Результат: number one number two number three number four

Что же именно произошло? Часть «for x» цикла «for» определяет переменную (называемую итератором) «$x», которая последовательно принимает значения «one», «two», «three», и «four» (по одному за один такт цикла). После присвоения каждого нового значения переменной «$x», выполняется тело цикла (код между словами «do» и «done»). В теле цикла мы выводим на печать значение переменной «$x». Заметим, что после слова «in» в конструкции «for» всегда стоит некий список. В данном примере мы указали четыре слова, но этот список может содержать имена файлов или даже шаблон (wildcard). В следующем примере показано как использовать шаблоны при инициализации итератора цикла:

#!/bin/bash for myfile in /etc/r* do if [ -d "$myfile" ] then echo "$myfile (dir)" else echo "$myfile" fi done результат: /etc/rc0.d (dir) /etc/rc1.d (dir) /etc/rc2.d (dir) /etc/rc3.d (dir) /etc/rc4.d (dir) /etc/rc5.d (dir) /etc/rc6.d (dir) /etc/rc.local /etc/rcS.d (dir) /etc/rearj.cfg /etc/reportbug.conf /etc/resolvconf (dir) /etc/resolv.conf /etc/rmt /etc/rpc /etc/rsyslog.conf /etc/rsyslog.d (dir)

Код этого цикла исполнится для каждого файла из /etc/ имя которого начинается с «r». Сначала bash найдет все такие файлы и заменит шаблон строкой /etc/rc0.d /etc/rc1.d /etc/rc2.d /etc/rc3.d /etc/rc4.d … /etc/rsyslog.d перед тем как приступить к выполнению цикла. В теле цикла для каждого файла из списка проверяется является ли этот файл директорией при помощи оператора «-d«. Если файл оказался директорией, рядом с его называнием печатается «(dir)».

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

for x in /etc/r. /var/lo* /home/drobbins/mystuff/* /tmp/$/* do cp $x /mnt/mydira done

Bash в этом примере подставляет значение переменной и раскрывает шаблоны. А затем копирует все файлы в заданную директорию.

До этого все примеры содержали шаблоны основанные на абсолютных путях, но можно использовать и относительные:

for x in ../* mystuff/* do echo "$x is a silly file" done

В этом примере bash раскрывает шаблон относительно текущей рабочей директории (не той в которой находится скрипт, а той которую показывает команда «pwd»). Поиграйтесь с этим скриптом, позапускайте его из разных директорий и посмотрите на результат.

Иногда может потребоваться запустить цикл по списку аргументов из командной строки. Вот как это делается:

#!/bin/bash for i in "$@" do echo "Вы написали: $." done результат: $ ./test.sh hello there you silly Вы написали: hello. Вы написали: there. Вы написали: you. Вы написали: silly.

В этом примере мы использовали переменную «$@» о которой говорили выше.

Арифметика в shell

Перед тем как приступить к разбору следующего вида циклической конструкции, научимся при помощи интерпретатора производить простые арифметические операции. Просто заключите арифметическое выражение в конструкцию «$(( ))» и bash посчитает ее значение. Вот несколько примеров:

$ echo $(( 100 / 3 )) 33 $ myvar="56" $ echo $(( $myvar + 12 )) 68 $ echo $(( $myvar — $myvar )) 0 $ myvar=$(( $myvar + 1 )) $ echo $myvar 57

Теперь, когда вы познакомились с вычислением арифметических выражений в shell, пришло время рассказать о циклических конструкциях «while» и «until».

Циклические конструкции с условиями («while» и «until»)

«while»–цикл исполняется пока выражение в квадратных скобках истинно. Он имеет следующий формат:

while [ условие ] do код done

В следующем примере тело цикла исполняется ровно 10 раз:

myvar=0 while [ $myvar -ne 10 ] do echo "$myvar" myvar=$(( $myvar + 1 )) done

После каждого выполнения кода тела цикла переменная «myvar» увеличивается на 1. Когда значение переменной становится равным 10, условие в квадратных скобках не выполняется и цикл прерывается.

«Until»–цикл очень похож на «while»–цикл: он повторяется пока выражение в квадратных скобках ложно. Вот пример «until»–цикла по функциональности идентичного «while»–циклу из предыдущего примера:

myvar=0 until [ $myvar -eq 10 ] do echo $myvar myvar=$(( $myvar + 1 )) done

Экстренный выход из цикла

Для экстренного выхода из «for», «while» или «until» цикла используется команда break. Для выхода из нескольких вложенных циклов — break N, где N — количество вложенных циклов.

name=0 while : do wget http://example.com/gallery/$.png [ $? -ne 0 ] && break done

В последнем примере: «while :» — бесконечный цикл. Двоеточие — это команда bash которая не делает ничего но всегда завершается успехом. Переменная $? содержит статус с которым завершилась последняя команда (подробнее о специальных переменных смотри man bash). В нашем случае код отличный от 0 обозначает что при скачивании файла произошла ошибка. Как только условие в квадратных скобках выполнено, интерпретатор переходит к исполнению команды стоящей после логического и (&&). Break прерывает выполнение цикла.

Читайте также:  Как возвести число под корень

Предпоследнюю строку предыдущего примера можно заменить на знакомую нам условную конструкцию «if» (помним, что в bash одно действие можно сделать несколькими разными способами):

то же самое но через условную конструкцию:

if [ $? -ne 0 ] then break fi

Или в одну строку

if [ $? -ne 0 ]; then break; fi

Да, конструкции можно записывать в одну строку, только нужно поставить несколько разделяющих знаков «точка с запятой». Но не стоит привыкать к такой форме записи — это усложняет читаемость кода.

Команда–переключатель «case»

Конструкция условного перехода «case» может оказаться очень полезной. Вот пример ее использования:

case "$" in gz) gzunpack $/$ ;; bz2) bz2unpack $/$ ;; *) echo "Формат архива не определен." exit ;; esac

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

Функции и пространство имен

В bash вы можете определять свои функции, как и в других языках программирования (C, Pascal…). Эти функции могут принимать аргументы, используя механизм очень похожий на механизм работы с аргументами командной строки. Вот пример определения простой функции:

tarview() < echo -n "Displaying contents of $1 " if [ $<1##*.>= tar ] then echo "(uncompressed tar)" tar tvf $1 elif [ $ <1##*.>= gz ] then echo "(gzip-compressed tar)" tar tzvf $1 elif [ $ <1##*.>= bz2 ] then echo "(bzip2-compressed tar)" cat $1 | bzip2 -d | tar tvf — fi >

Выше мы определили функцию с именем «tarview», которая принимает один аргумент — имя тарбола. Эта функция определяет вид тарбола (без сжатия, сжатый gzip-ом или bzip2) по расширению, затем печатает этот тип и показывает содержимое архива. Если формат определить не удалось, выводится соответствующее сообщение. Вот пример вызова функции:

$ ./tarview.sh shorten.tar.gz Displaying contents of shorten.tar.gz (gzip-compressed tar) drwxr-xr-x ajr/abbot 0 1999-02-27 16:17 shorten-2.3a/ -rw-r—r— ajr/abbot 1143 1997-09-04 04:06 shorten-2.3a/Makefile -rw-r—r— ajr/abbot 1199 1996-02-04 12:24 shorten-2.3a/INSTALL -rw-r—r— ajr/abbot 839 1996-05-29 00:19 shorten-2.3a/LICENSE .

Как вы видите, обращение к аргументам внутри функции происходит по тем же именам как и к аргументам командной строки внутри скрипта. Переменная «$#» содержит количество переданных функции аргументов. Единственное что остается по-прежнему — переменная «$0«. Она содержит название скрипта при вызове функции из скрипта или строку «bash» при вызове функции напрямую из командной строки.

Вызвать функцию из командной строки можно следующим образом: сохраняем код функции в файл (например с названием «/myfunc.txt») а затем даем следующую команду:

или что тоже самое

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

/.bash_profile, тогда вы сможете вызывать ее из командной строки в любое время при следующем логине.

Пространство имен

Часто возникает потребность создать переменную окружения внутри функции. В большинстве компилируемых языков (например Си), когда вы создаете переменную внутри функции, она попадает в отдельное пространство имен этой функции. Например, если вы напишите функцию «my function» на C и внутри этой функции создадите переменную «x», то она никак не повлияет на переменную с тем же именем «x», созданную вне функции «myfunction».

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

Результатом исполнения этого кода будет строка «ne two three three«, показывающая что переменная «myvar», созданная внутри функции перезаписала значение глобальной переменной «myvar» и что последнее значение итератора «x» равное «three» продолжило существование даже после завершения функции.

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

Результатом выполнения этого кода будет строка «hello» — значение глобальной переменной «myvar» (на которую никак не повлияла локальная переменная «myvar», созданная внутри функции), а локальная переменная «x» перестает существовать после завершения функции.

Единственное условие при котором вы не должны использовать локальные переменные внутри функций — если хотите изменить значение глобальной переменной.

Подведение итогов

Вот и все. Теперь вы имеете представление о программировании в bash и можете писать свои скрипты. За более подробной информацией обращайтесь к справке man bash или к руководству Advanced Bash-Scripting Guide

скажем, у меня есть скрипт, который вызывается с этой строки:

каков принятый способ разбора этого так, что в каждом случае (или некотором сочетании двух) $v , $f и $d все будет установлено значение true и $outFile будет равна /fizz/someOtherFile ?

29 ответов:

два распространенных способа передачи аргументов пары ключ-значение:

Bash через пробел (например, —option argument ) (без getopt[s])

использование ./myscript.sh -e conf -s /etc -l /usr/lib /etc/hosts

Bash Equals-Separated (например, —option=argument ) (без getopt[s])

использование ./myscript.sh -e=conf -s=/etc -l=/usr/lib /etc/hosts

чтобы лучше понять $ поиск "удаление подстроки" в данное руководство. Это функционально эквивалентно `sed ‘s/[^=]*=//’ который вызывает ненужный подпроцесс или `echo "$i" | sed ‘s/[^=]*=//’` которых звонки два излишне подпроцессов.

getopt(1) ограничения (старые, сравнительно недавно getopt версии):

  • не может обрабатывать аргументы, которые являются пустые строки
  • не могу справиться аргументы со встроенными пробелами

последние getopt версии не имеют этих ограничений.

кроме того, оболочка POSIX (и другие) предлагают getopts , который не имеет этих ограничений. Вот такой упрощенный getopts пример:

преимущества getopts являются:

  1. это более портативный, и будет работать в других оболочках, как dash .
  2. оно может отрегулировать множественные одиночные варианты как -vf filename в типичном пути Unix, автоматически.

недостаток getopts это то, что он может обрабатывать только короткие варианты ( -h , а не —help ) без дополнительного кода.

есть getopts tutorial, который объясняет, что все синтаксис и переменные означают. В Баш, есть также help getopts , что может быть информативным.

нет ответа упоминает enhanced getopt. А то топ-проголосовали ответ вводит в заблуждение: он игнорирует -⁠vfd стиль коротких опций (запрошенных OP), опции после позиционных аргументов (также запрошенных OP), и он игнорирует ошибки синтаксического анализа. Вместо этого:

  • используйте enhanced getopt из util-linux или ранее GNU glibc. 1
  • он работает с getopt_long() в C функция GNU glibc.
  • и все полезные отличительные особенности (другие не имеют их):
  • обрабатывает пробелы, кавычки символов и даже двоичные аргументы 2
  • он может обрабатывать параметры в конце: script.sh -o outFile file1 file2 -v
  • позволяет = -стиль длинные варианты: script.sh —outfile=fileOut —infile fileIn
  • уже так стар 3 что никакая система GNU не является хватает этого (например, Linux имеет его).
  • вы можете проверить его существование с: getopt —test → возвращаемое значение 4.
  • другое getopt или shell-builtin getopts имеют ограниченное применение.
  • 1 enhanced getopt доступен на большинстве "bash-систем", включая Cygwin; on ОС Х попробовать brew установить gnu-getopt или sudo port install getopt
    2 POSIX exec() соглашения не имеют надежного способа передать двоичный NULL в аргументах командной строки; эти байты преждевременно заканчивают аргумент
    3 первая версия выпущена в 1997 году или раньше (я только проследил его до 1997 года)

    использование myscript.sh -p=my_prefix -s=dirname -l=libname

    чтобы лучше понять $ поиск "удаление подстроки" в данное руководство. Это функционально эквивалентно `sed ‘s/[^=]*=//’ который вызывает ненужный подпроцесс или `echo "$i" | sed ‘s/[^=]*=//’` которых звонки два излишне подпроцессов.

    getopt() / getopts() — хороший выбор. Украдено у здесь:

    простое использование "getopt" показано в этом мини-скрипте:

    мы говорили, что любой из, -b, — c или-d будет разрешено, но за этим-c следует аргумент ("c:" говорит это).

    если мы назовем это " g " и попробуем:

    начнем с двух аргументов, и "getopt" разбивает варианты и ставит каждый в свой аргумент. Это также добавлен."—"

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

    • ручки -n arg и —name=arg
    • позволяет аргументы в конце
    • показывает вменяемые ошибки, если что-то неправильно написал
    • совместимость, не использует башизмы
    • читаемо, не требует поддержания состояния в цикле
    Читайте также:  Как проверить айпад на оригинальность

    надеюсь, что это полезно кому-то.

    script.sh

    использование:

    Я около 4 лет на этот вопрос, но хочу вернуть. Я использовал более ранние ответы в качестве отправной точки для приведения в порядок моего старого разбора adhoc param. Затем я переработал следующий код шаблона. Он обрабатывает как длинные, так и короткие параметры, используя аргументы = или пробел, а также несколько коротких параметров, сгруппированных вместе. Наконец он вновь вставляет любой не-парам аргументов в $1,$2.. переменная. Я надеюсь, что это полезно.

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

    Это позволяет вам иметь как разделенные пробелом параметры / значения, так и равные определенные значения.

    таким образом, вы можете запустить свой скрипт с помощью:

    и оба должны иметь один и тот же конец результат.

    позволяет для обоих-arg=value и-arg value

    работает с любым именем arg, которое вы можете использовать в bash

    • означает-А или-арг или —арг или-А-р-г или что-то еще

    чисто Баш. Нет необходимости учиться / использовать getopt или getopts

    не удается объединить аргументы

    • означает нет-abc. Вы должны сделать-а-б-с

    Это единственные плюсы / минусы, которые я могу придумать с моей головы

    Я нашел дело, чтобы написать портативный разбор в скриптах так расстраивает, что я написал Argbash — генератор кода FOSS, который может генерировать аргументы-разбор кода для вашего скрипта плюс он имеет некоторые приятные функции:

    Я думаю, что этот достаточно прост в использовании:

    расширяя отличный ответ от @guneysus, вот настройка, которая позволяет пользователю использовать любой синтаксис, который они предпочитают, например

    то есть равные могут быть заменены пробелами.

    эта "нечеткая интерпретация" может быть вам не по душе, но если вы делаете скрипты, которые взаимозаменяемы с другими утилитами (как в случае с моим, который должен работать с ffmpeg), гибкость полезна.

    getopts отлично работает, если #1 у вас установлен и #2 вы собираетесь запустить его на той же платформе. OSX и Linux (например) ведут себя по-разному в этом отношении.

    вот (не getopts) решение, которое поддерживает equals, non-equals и boolean флаги. Например, вы можете запустить свой скрипт следующим образом:

    Я даю вам функцию parse_params это будет анализировать параметры из командной строки.

    1. это чистое решение Bash, никаких дополнительных утилит.
    2. не загрязняет глобальную область видимости.
    3. легко возвращает вам простые в использовании переменные,которые вы могли бы построить дальнейшую логику.
    4. количество тире перед парами не имеет значения ( —all равна -all равна all=all )

    скрипт ниже является копипастом рабочая демонстрация. Смотрите

    вот как я делаю в функции, чтобы избежать разрыва getopts запустить в то же время где-то выше в стеке:

    EasyOptions не требует никакого разбора:

    Я хотел бы предложить свою версию разбора вариант, который позволяет следующее:

    также позволяет это (может быть нежелательным):

    вы должны решить перед использованием, если = будет использоваться на опции или нет. Это необходимо для поддержания кода в чистоте (ish).

    отметим, что getopt(1) была короткая ошибка жизни от AT & T.

    getopt был создан в 1984 году, но уже похоронен в 1986 году, потому что он не был действительно полезен.

    доказательство того, что getopt очень устарело это getopt(1) man страница все еще упоминает "$*" вместо "[email protected]" , который был добавлен в оболочку Борна в 1986 году вместе с getopts(1) оболочка встроена для того, чтобы иметь дело с аргументами с пробелами внутри.

    кстати: если вы заинтересованы в разборе длинных вариантов в скриптах оболочки, это может быть интересно знать, что getopt(3) реализация из libc (Solaris) и ksh93 оба добавили единую реализацию длинных опций, которая поддерживает длинные опции в качестве псевдонимов для коротких опций. Это вызывает ksh93 и Bourne Shell реализовать единый интерфейс для длинных опций через getopts .

    пример для длинных опций, взятых из справочной страницы Bourne Shell:

    getopts "f:(file)(input-file)o:(output-file)" OPTX "[email protected]"

    показывает как долго псевдонимы опций могут использоваться как в Bourne Shell, так и в ksh93.

    смотрите man-страницу недавнего Bourne Shell:

    и man-страница для getopt (3) из OpenSolaris:

    и последнее, страница руководства getopt(1) для проверки устаревших $*:

    смешивание позиционных и флаговых аргументов

    —param=arg (равно разделено)

    свободное смешивание флагов между позиционными аргументами:

    может быть выполнено с достаточно лаконичным подходом:

    —param arg (пробел)

    обычно яснее не смешивать —flag=value и —flag value стили.

    это немного рискованно читать, но все же действительно

    предположим, что мы создаем сценарий оболочки с именем test_args.sh как следовать

    после выполнения следующей команды:

    использовать модуль "аргументы" от bash-modules

    Это также может быть полезно знать, вы можете установить значение и если кто-то обеспечивает ввод, по умолчанию это значение..

    myscript.sh -f ./ serverlist.txt или просто ./myscript.sh (и он принимает значения по умолчанию)

    аналогично решение Бруно Броноски опубликовано это без использования getopt(s) .

    главная отличительная особенность моего решения заключается в том, что он позволяет иметь варианты, сцепленные вместе так же, как tar -xzf foo.tar.gz равна tar -x -z -f foo.tar.gz . И так же, как в tar , ps etc. ведущий дефис является необязательным для блока коротких опций (но это можно легко изменить). Также поддерживаются длинные опции (но когда блок начинается с одного, то требуются два ведущих дефиса).

    код с Пример

    для примера использования см. примеры ниже.

    положение опций с аргументами

    для чего его стоит там варианты с аргументами не являются последними (только длинные варианты должны быть). Так что пока, например, в tar (по крайней мере, в некоторых реализациях) в f параметры должны быть последними, потому что имя файла следует ( tar xzf bar.tar.gz работает, а tar xfz bar.tar.gz не делает) это не так здесь (см. Более поздние примеры).

    несколько вариантов с аргументами

    в качестве еще одного бонуса параметры опции потребляются в порядке следования опций по параметрам с требуемыми опциями. Просто посмотрите на вывод моего скрипта здесь с помощью командной строки abc X Y Z (или -abc X Y Z ):

    длинные параметры объединены как ну

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

    • -cba Z Y X
    • cba Z Y X
    • -cb-aaa-0-args Z Y X
    • -c-bbb-1-args Z Y X -a
    • —ccc-2-args Z Y -ba X
    • c Z Y b X a
    • -c Z Y -b X -a
    • —ccc-2-args Z Y —bbb-1-args X —aaa-0-args

    все это приводит к:

    не в этом решение

    дополнительные аргументы

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

    все становится еще сложнее с несколькими возможными параметрами. Я бы посоветовал не делать варианты, пытаясь быть умным, определяя, может ли аргумент быть для него или нет (например, с опцией просто принимает число в качестве необязательного аргумента), потому что это может сломаться в будущем.

    я лично предпочитаю дополнительные опции вместо необязательных аргументов.

    аргументы опции введены со знаком равенства

    как и с дополнительными аргументами, я не поклонник этого (кстати, есть ли нить для обсуждения плюсов/минусов разных стилей параметров?) но если вы хотите этого, вы, вероятно, могли бы реализовать его самостоятельно просто как сделано в http://mywiki.wooledge.org/BashFAQ/035#Manual_loop с —long-with-arg=?* case заявление, а затем зачистка знака равенства (это кстати сайт, который говорит, что создание параметра конкатенации возможно с некоторым усилием, но "оставил [его] в качестве упражнения для читателя", который заставил меня взять их на слово, но я начал с нуля).

    другие Примечания

    POSIX-совместимый, работает даже на древних настройках Busybox, с которыми мне приходилось иметь дело (например cut , head и getopts отсутствует).

    Рекомендуем к прочтению

    Добавить комментарий

    Ваш адрес email не будет опубликован.