Коды результата, объединение в цепочку и флаги

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

Работа с кодами результата

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

Каждый раз, когда команда выполняется (включая открытую оболочку скобки, встроенную используемый в качестве части if и while операторы), код результата сгенерирован. Если команда выходит успешно, результатом является обычно нуль (0). Если выходы команды с ошибкой, код результата будет варьироваться согласно инструменту. (См. документацию для рассматриваемого инструмента для списка кодов результата.) Возможный диапазон кодов результата 0-255.

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

if ls mysillyfilename ; then
    echo "File exists."
fi

Второй путь путем тестирования последнего возвращенного статуса выхода. Статус выхода сохранен в переменной оболочки $?. Например:

ls mysillyfilename
if [ $? = 0 ] ; then
    echo "File exists."
fi

Третий путь путем использования в своих интересах «и» оператор:

ls mysillyfilename && echo "File exists."

Эти три примера кода должны генерировать тот же вывод. Третий метод объяснен далее в Объединении в цепочку Выполнения.

Объединение в цепочку выполнения

Оболочка предоставляет трем операторам для объединения в цепочку execution:and (&&), или (||) и не (!).

И (&&)

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

Или (||)

Если команда налево успешно выполняется (имеет нулевой статус выхода), команда вправо не выполняется. Если команда к левым сбоям, вправо выполняется команда. Если крайняя левая команда успешно выполняется, статус выхода, возвращенный этим оператором, является нулем. Иначе, статус выхода возвратился, статус выхода команды направо от оператора.

Не (!)

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

Эти три оператора показаны в следующем отрывке:

ls / || ! ls mysillyfilename && echo "Whatever."

Правила приоритета оператора в сценариях Оболочки Bourne очень отличаются от тех в C. Круглые скобки оценены сначала, поскольку они могут использоваться для переопределения группировки операторов. После этого, однако, оценка операторов происходит в порядке слева направо.

Например, следующая строка перечисляет все файлы в корневом каталоге, затем отзывается эхом, “Это - мальчик”:

 ls / || ls /xy && echo "It's a boy"

|| оператор имеет приоритет по && оператор из-за слева направо правил оценки. Оценка ярлыков оболочки || оператор. Таким образом, потому что ls / всегда успешно выполняется, || оператор вызывает второе ls быть пропущенным полностью, и оператор до && оператор оценивает к true (0). Это значение тогда объединено с оператором эха после него && оператор. Таким образом оператор эха выполняется впоследствии.

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

ls / || ( ls /nonexistentfile && echo "file exists" )

В этом случае, потому что первое ls оператор успешен, остаток от оператора пропускается. Если Вы заменяете ls / с false, неработающее перечисление nonexistentfile генерирует сообщение об ошибке и ненулевой статус выхода, поочередно вызывающий echo оператор, который все еще будет пропущен.

Конечно, существование этих операторов также означает, что Вы могли записать if оператор, фактически не используя if ключевое слово, как показано в следующем отрывке:

FOO=3
[ $FOO -eq 3 ] && echo "three"

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

Обработка флагов и параметров

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

Специальные переменные мультипараметра

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

$#.

Содержит число параметров.

$*.

Расширяется до списка параметров, запускающихся с $1.

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

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

Совместимость Примечание: В ЭКС-АН-ПРОВАНСЕ при окружении этой переменной кавычками, оболочка обертывает каждый отдельный параметр с кавычками, когда это разворачивает переменную.

$@.

Расширяется до списка параметров, запускающихся с $1.

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

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

Кроме того, если эта переменная появляется в метках двойной кавычки вместе с другим текстом ("BLAH$@BLAH", например), часть строки до $@ предварительно ожидается к первому параметру и части строки после $@ добавляется к последнему параметру.

Оболочка C Примечание: Эта переменная не существует в оболочке C. Использовать $* вместо этого.

Следующие листинги кода демонстрируют использование этих параметров и тонких различий между ними.

Перечисление 5-1 00_listargs.sh  

#!/bin/sh
 
for i in "$@" ; do
echo ARG $i
done

Перечисление 5-2 01_testargs.sh  

#!/bin/sh
 
IFS="
"
 
echo "COUNT: $#"
echo
echo '\$*'
./00_listargs.sh $*
echo
echo '"\$*"'
./00_listargs.sh "$*"
echo
echo '$@'
./00_listargs.sh $@
echo
echo '"$@"'
./00_listargs.sh "$@"
echo
echo '"foo bar$*bar foo"'
./00_listargs.sh "foo bar$*bar foo"
echo
echo '"foo bar$@bar foo"'
./00_listargs.sh "foo bar$@bar foo"

Сохраните эти сценарии с показанными именами файлов, затем выполните их путем ввода ./01_testargs.sh This is a "silly test" и отметьте различия в способе, которым ведут себя эти переменные.

Встроенный сдвиг

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

Следующий сценарий демонстрирует встроенный сдвиг:

Перечисление 5-3 02_shift.sh  

#!/bin/sh
 
echo "\$1: $1 \$2: $2 \$3: $3 \$4: $4 \$5: $5 \$6: $6"
 
shift
 
echo "\$1: $1 \$2: $2 \$3: $3 \$4: $4 \$5: $5 \$6: $6"
 
shift 2
 
echo "\$1: $1 \$2: $2 \$3: $3 \$4: $4 \$5: $5 \$6: $6"

Выполните этот сценарий путем ввода ./02_shift.sh The quick brown fox jumped over the lazy dog. и заметьте, как изменяются параметры. Первоначально, первые шесть параметров "The quick brown fox jumped over". После первого shift оператор, первые шесть параметров "quick brown fox jumped over the". После второго оператора сдвига первые шесть параметров "fox jumped over the lazy dog".

getopts встроенное и getopt команда

getopts встроенный и getopt команда оба обрабатывает список параметров способом, который подобен getopt функция в C. Если Вы пишете сценарий Оболочки Bourne, getopts встроенный строго рекомендуется, потому что это быстрее, более безопасно, и более гибко. (Если Вы пишете сценарий оболочки C, getopts встроенный не доступно.)

Оба getopt и getopts возьмите строку опции в качестве параметра. Эта строка опции создается следующим образом:

Простой флаг

Просто используйте букву флага. Например, для добавления "-f" отметьте, добавьте букву "f" к строке опции.

Флаг с параметром

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

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

getopts Встроенное

getopts встроенный помещает Ваш сценарий в управление процесса парсинга параметра. Каждый вызов к getopts возвращает единственный флаг и, где применимо, параметр тому флагу. Синтаксис следующие:

getopts opt_string user_specified_variable [args]

Строка опции описана выше в getopts встроенном и getopt команде. Указанная пользователями переменная описана ниже. getopts встроенный может также дополнительно взять список параметров процессу. Необходимо обычно опускать это.

getopts встроенный изменяет значения следующих переменных:

user_specified_variable

Право преимущественной покупки Вы передаете getopts имя переменной. getopts переменная помещает сам флаг в указанную переменную (без ведущего дефиса).

OPTARG

Значение аргумента связалось с текущим флагом (если применимо).

OPTERR

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

OPTIND

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

Например, следующий сценарий является сырым вариантом ls команда. Это берет дополнительное -l флаг, включающий длинные списки и дополнительное -o флаг, содержащий имя файла, в который это пишет свой вывод. Если никакой выходной файл не указан, это пишет свой вывод в стандартный вывод. Это также берет дополнительный путь или список путей, передающихся ls как есть.

Перечисление 5-4 03_getopts.sh  

#!/bin/sh
 
DO_LONG=""
 
# Start processing options at index 1.
OPTIND=1
# OPTERR=1
OUTPUT_FILE=""
while getopts ":hlo:" VALUE "$@" ; do
 
    echo "GOT FLAG $VALUE"
 
    if [ "$VALUE" = "h" ] ; then
        echo "Usage: $0 [-l] [-o outputfile] [path ...]"
        exit 1
    fi
    if [ "$VALUE" = "l" ] ; then
        DO_LONG="-l"
    fi
    if [ "$VALUE" = "o" ] ; then
        echo "Set output file to \"$OPTARG\""
        OUTPUT_FILE="$OPTARG"
    fi
    # The getopt routine returns a colon when it encounters
    # a flag that should have an argument but doesn't.  It
    # returns the errant flag in the OPTARG variable.
    if [ "$VALUE" = ":" ] ; then
        echo "Flag -$OPTARG requires an argument."
        echo "Usage: $0 [-l] [-o outputfile] [path ...]"
        exit 1
    fi
    # The getopt routine returns a question mark when it
    # encounters an unknown flag.  It returns the unknown
    # flag in the OPTARG variable.
    if [ "$VALUE" = "?" ] ; then
        echo "Unknown flag -$OPTARG detected."
        echo "Usage: $0 [-l] [-o outputfile] [path ...]"
        exit 1
    fi
done
 
# The first non-flag argument is at index $OPTIND, so shift one fewer
# to move it into $1
shift `expr $OPTIND - 1`
 
if [ "$OUTPUT_FILE" = "" ] ; then
    ls $DO_LONG "$@"
else
    ls $DO_LONG "$@" > $OUTPUT_FILE
fi
 
exit $?

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

getopt Команда

getopt команда проявляет другой подход, чем getopts встроенный. Это обрабатывает весь список аргументов сразу и сообщает, соответствует ли список аргументов список допустимых флагов или нет. Если список аргументов соответствует, getopt канонизирует список аргументов, помещая флаги и их дополнительные аргументы сначала (до любых параметров нефлага), сопровождаемый синглом "--" параметр, чтобы указать, что больше нет флагов для обработки.

Синтаксис getopt команда следующие:

getopt opt_string args

Следующий отрывок ведет себя во многом как тот в Перечислении 5-4. В отличие от этого в том примере, не возможно программно обнаружить природу ошибок (недостающие параметры или недопустимые флаги).

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

Перечисление 5-5 01_getopt.csh  

#!/bin/csh
 
set OUTPUT_FILE=""
set DO_LONG=""
 
set argv=`getopt "hlo:" $*`
 
if ( $status != 0 ) then
    echo "Usage: $0 [-l] [-o outputfile] [path ...]"
    exit 1
endif
 
while ( "$1" != "--" )
        echo "GOT FLAG $1"
    switch($1)
        case "-h":
            echo "Usage: $0 [-l] [-o outputfile] [path ...]"
            exit 1
        case "-o":
            set OUTPUT_FILE="$2"
            shift
            breaksw
        case "-l":
            set DO_LONG="-l"
            breaksw
    endsw
    shift
end
 
shift # remove trailing --
 
# echo "ARGS: $*"
 
if ( "$OUTPUT_FILE" == "" ) then
    ls $DO_LONG $*
else
    ls $DO_LONG $* > $OUTPUT_FILE
endif
 
exit $status