Bash Shell Script - Перевірте прапор і захопіть його значення


80

Я намагаюся зробити сценарій оболонки, який призначений для запуску таким чином:

script.sh -t application

По-перше, у своєму сценарії я хочу перевірити, чи сценарій не запускався з прапором -t. Наприклад, якщо він був запущений без такого прапора, я хочу, щоб він помилився:

script.sh

По-друге, припускаючи, що є прапор -t, я хочу захопити значення і зберегти його у змінній, яку я можу використовувати у своєму сценарії, наприклад, ось так:

FLAG="application"

Поки що єдиним прогресом, який я зміг досягти в будь-якому з цього, є те, що $ @ захоплює всі аргументи командного рядка, але я не знаю, як це пов'язано з прапорами, чи це взагалі можливо.


Відповіді:


128

Вам слід прочитати цей підручник з getopts .

Приклад з -aперемикачем, який вимагає аргументу:

#!/bin/bash

while getopts ":a:" opt; do
  case $opt in
    a)
      echo "-a was triggered, Parameter: $OPTARG" >&2
      ;;
    \?)
      echo "Invalid option: -$OPTARG" >&2
      exit 1
      ;;
    :)
      echo "Option -$OPTARG requires an argument." >&2
      exit 1
      ;;
  esac
done

Як Greybot сказав ( getopt! = getopts):

Зовнішня команда getopt (1) ніколи не безпечна у використанні, якщо ви не знаєте, що це GNU getopt, ви не викликаєте її спеціально для GNU і не гарантуєте, що GETOPT_COMPATIBLE відсутній у середовищі. Замість цього використовуйте getopts (вбудований в оболонку) або просто перебирайте позиційні параметри.


1
getoptsє bashвбудованим, але існує також зовнішня getoptпрограма, яка повинна працювати з будь-якою сумісною з POSIX оболонкою.
chepner

3
Зовнішня команда getopt (1) ніколи не безпечна у використанні, якщо ви не знаєте, що це GNU getopt, ви не викликаєте її спеціально для GNU і не гарантуєте, що GETOPT_COMPATIBLE відсутній у середовищі. Замість цього використовуйте getopts (вбудований в оболонку) або просто перебирайте позиційні параметри.
Gilles Quenot

Вибачте, що мені це потрібно для мене. Якщо це вбудований bash, чи буде це працювати для оболонки в debian
Джиммі

getoptsПідручник простий і корисні приклади. Безумовно, варто прочитати, якщо ви натрапите на цей пост.
Steven C. Howell

Погоджуюсь із попередніми коментарями: підручник з getopts серйозно коштує часу, витраченого на його читання, включаючи коментарі, деякі з яких надають вдосконалене / альтернативне використання.
François POYER

32

Використовуйте $#для отримання кількості аргументів, якщо вона нерівна 2, недостатньо аргументів:

if [ $# -ne 2 ]; then
   usage;
fi

Далі перевірте, чи $1дорівнює -t, інакше було використано невідомий прапор:

if [ "$1" != "-t" ]; then
  usage;
fi

Нарешті, зберігайте $2у FLAG:

FLAG=$2

Примітка: usage()це якась функція, що відображає синтаксис. Наприклад:

function usage {
   cat << EOF
Usage: script.sh -t <application>

Performs some activity
EOF
   exit 1
}

Це може спрацювати, але що відбувається, коли я хочу використовувати кілька прапорів?
killjoy

Це стає все більш складним і має сенс використовувати getopts команди як описано в відповіді @GillesQuenot
Veger

11

Ось узагальнений простий інтерфейс аргументу команди, який ви можете вставити вгору всіх ваших сценаріїв.

#!/bin/bash

declare -A flags
declare -A booleans
args=()

while [ "$1" ];
do
    arg=$1
    if [ "${1:0:1}" == "-" ]
    then
      shift
      rev=$(echo "$arg" | rev)
      if [ -z "$1" ] || [ "${1:0:1}" == "-" ] || [ "${rev:0:1}" == ":" ]
      then
        bool=$(echo ${arg:1} | sed s/://g)
        booleans[$bool]=true
        echo \"$bool\" is boolean
      else
        value=$1
        flags[${arg:1}]=$value
        shift
        echo \"$arg\" is flag with value \"$value\"
      fi
    else
      args+=("$arg")
      shift
      echo \"$arg\" is an arg
    fi
done


echo -e "\n"
echo booleans: ${booleans[@]}
echo flags: ${flags[@]}
echo args: ${args[@]}

echo -e "\nBoolean types:\n\tPrecedes Flag(pf): ${booleans[pf]}\n\tFinal Arg(f): ${booleans[f]}\n\tColon Terminated(Ct): ${booleans[Ct]}\n\tNot Mentioned(nm): ${boolean[nm]}"
echo -e "\nFlag: myFlag => ${flags["myFlag"]}"
echo -e "\nArgs: one: ${args[0]}, two: ${args[1]}, three: ${args[2]}"

Запустивши команду:

bashScript.sh firstArg -pf -myFlag "my flag value" secondArg -Ct: thirdArg -f

Результат буде таким:

"firstArg" is an arg
"pf" is boolean
"-myFlag" is flag with value "my flag value"
"secondArg" is an arg
"Ct" is boolean
"thirdArg" is an arg
"f" is boolean


booleans: true true true
flags: my flag value
args: firstArg secondArg thirdArg

Boolean types:
    Precedes Flag(pf): true
    Final Arg(f): true
    Colon Terminated(Ct): true
    Not Mentioned(nm): 

Flag: myFlag => my flag value

Args: one => firstArg, two => secondArg, three => thirdArg

В основному аргументи поділяються на булеві значення прапорів та загальні аргументи. Роблячи це таким чином, користувач може розміщувати прапори та логічні значення будь-де, доки він / вона зберігає загальні аргументи (якщо вони є) у зазначеному порядку.

Дозвольте мені і тепер вам більше ніколи не мати справу з розбором аргументів bash!

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

Це було надзвичайно корисно за останній рік. Тепер він може імітувати область дії, додаючи до змінних параметр сфери дії.

Просто назвіть сценарій як

replace() (
  source $FUTIL_REL_DIR/commandParser.sh -scope ${FUNCNAME[0]} "$@"
  echo ${replaceFlags[f]}
  echo ${replaceBooleans[b]}
)

Не схоже, що я реалізував область аргументів, не знаю, чому, мабуть, мені це ще не потрібно.


2
Це чудова відповідь: я роблю це на крок далі, поміщаючи його в скрипт, який потім псевдонім arg_handler у своєму файлі .bashrc. Потім у верхній частині кожного сценарію я вставляю:source arg_handler; arg_handler "$@"
J.Warren

Чи не повинно -pf бути двома окремими прапорцями, оскільки це не продовжується двома тиреми? Як на п і ф.
James Trickey

1

Спробуйте shFlags - розширена бібліотека прапорів командного рядка для скриптів оболонки Unix.

http://code.google.com/p/shflags/

Це дуже добре і дуже гнучко.

ТИПИ ПРАПОРІВ: Це перелік ВИЗНАЧЕНИХ _ *, які ви можете зробити. Усі прапори беруть ім'я, значення за замовчуванням, рядок довідки та необов'язкове "коротке" ім'я (однобуквене ім'я). Деякі прапори мають інші аргументи, які описуються разом із прапором.

DEFINE_string: приймає будь-який вхід і інтерпретує його як рядок.

DEFINE_boolean: зазвичай не приймає жодного аргументу: скажімо --myflag, щоб встановити FLAGS_myflag на true, або --nomyflag, щоб FLAGS_myflag встановити на false. Крім того, ви можете сказати --myflag = true або --myflag = t або --myflag = 0 або --myflag = false або --myflag = f або --myflag = 1 Передача опції має той самий ефект, що і передача варіант один раз.

DEFINE_float: бере вхідні дані та інтерпретує їх як число з плаваючою комою. Оскільки оболонка не підтримує поплавці як такі, введення просто перевіряється як дійсне значення з плаваючою комою.

DEFINE_integer: бере вхід і інтерпретує його як ціле число.

СПЕЦІАЛЬНІ ПРАПОРИ: Є кілька прапорів, які мають особливе значення: --help (або -?) Друкує список усіх прапорів, зручним для читання --flagfile = foo читати прапори з foo. (ще не реалізовано) - як і в getopt (), завершує обробку прапора

ПРИКЛАД ВИКОРИСТАННЯ:

-- begin hello.sh --
 ! /bin/sh
. ./shflags
DEFINE_string name 'world' "somebody's name" n
FLAGS "$@" || exit $?
eval set -- "${FLAGS_ARGV}"
echo "Hello, ${FLAGS_name}."
-- end hello.sh --

$ ./hello.sh -n Kate
Hello, Kate.

Примітка: Я взяв цей текст із документації shflags

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.