Подтвердить что ты не робот

Как создать bash script, который принимает аргументы?

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

В идеале я хотел бы иметь script, который получает аргументы в этой форме:

script.sh [optional arguments] [anything required]

например

script.sh -rvx output_file.txt

где script говорит, что у вас есть выходной файл. Есть ли простой способ сделать это?

Насколько я знаю, с getopts это должно выглядеть так: script.sh -rvx -f output_file.txt, и это просто не очень чисто.

Я также могу использовать python, если это необходимо, но имеет только доступную версию 2.4, которая немного устарела.

4b9b3361

Ответ 1

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

#!/bin/bash

eval set -- $(getopt -n $0 -o "-rvxl:" -- "[email protected]")

declare r v x l
declare -a files
while [ $# -gt 0 ] ; do
        case "$1" in
                -r) r=1 ; shift ;;
                -v) v=1 ; shift ;;
                -x) x=1 ; shift ;;
                -l) shift ; l="$1" ; shift ;;
                --) shift ;;
                -*) echo "bad option '$1'" ; exit 1 ;;
                *) files=("${files[@]}" "$1") ; shift ;;
         esac
done

if [ ${#files} -eq 0 ] ; then
        echo output file required
        exit 1
fi

[ ! -z "$r" ] && echo "r on"
[ ! -z "$v" ] && echo "v on"
[ ! -z "$x" ] && echo "x on"

[ ! -z "$l" ] && echo "l == $l"

echo "output file(s): ${files[@]}"

EDIT: для полноты я привел пример обработки опции, требующей аргумента.

Ответ 2

Если вы используете getops, просто смените $OPTIND-1 после вашего оператора case. Тогда то, что осталось в $*, будет всем остальным, что, вероятно, вам нужно.

shift $(( ${OPTIND} - 1 )); echo "${*}"

Ответ 3

Вы страдаете от иллюзий; использование getopts не требует обязательных аргументов с префиксом буквы флага. Я попытался найти подходящий пример из моего корпуса скриптов; это полуприличное приближение. Он называется rcsunco и используется для отмены выписки из RCS. Я не изменял это через некоторое время, я вижу; Я использую его довольно часто (потому что я еще не полностью перешел из RCS).

#!/bin/sh
#
#   "@(#)$Id: rcsunco.sh,v 2.1 2002/08/03 07:41:00 jleffler Exp $"
#
#   Cancel RCS checkout

# -V print version number
# -n do not remove or rename checked out file (like SCCS unget) (default)
# -r remove checked out file (default)
# -k keep checked out file as $file.keep
# -g checkout (unlocked) file after clean-up
# -q quiet checkout

: ${RCS:=rcs}
: ${CO:=co}

remove=yes
keep=no
get=no
quiet=

while getopts gknqrV opt
do
    case $opt in
    V)  echo "`basename $0 .sh`: RCSUNCO Version $Revision: 2.1 $ ($Date: 2002/08/03 07:41:00 $)" |
        rcsmunger
        exit 0;;
    g)  get=yes;;
    k)  keep=yes;;
    n)  remove=no;;
    q)  quiet=-q;;
    r)  remove=yes;;
    *)  echo "Usage: `basename $0 .sh` [-{n|g}][-{r|k}] file [...]" 1>&2
        exit 1;;
    esac
done

shift $(($OPTIND-1))

for file in $*
do
    rfile=$(rfile $file)
    xfile=$(xfile $rfile)
    if $RCS -u $rfile
    then
        if [ $keep = yes ]
        then
            if [ -f $xfile ]
            then
                mv $xfile $xfile.keep
                echo "$xfile saved in $xfile.keep"
            fi
        elif [ $remove = yes ]
        then rm -f $xfile
        fi
        if [ $get = yes ] && [ $remove = yes -o $keep = yes ]
        then $CO $quiet $rfile
        fi
    fi
done

Это только полупринятое приближение; script тихо ничего не делает, если вы не укажете никаких имен файлов после дополнительных аргументов. Однако, если вам нужно, вы можете проверить, что обязательные аргументы присутствуют после "shift". У другого script у меня есть обязательные аргументы. Он содержит:

...
shift $(($OPTIND - 1))

case $# in
2)  case $1 in
    install)    MODE=Installation;;
    uninstall)  MODE=Uninstallation;;
    *)          usage;;
    esac;;
*)  usage;;
esac

Итак, эта команда (jlss) может принимать необязательные аргументы, такие как -d $HOME, но требует либо install, либо uninstall, за которым следует имя что-то для установки. Основной способ использования:

jlss install program

Но дополнительный режим:

jlss -d $HOME -u me -g mine -x -p install program

Я не показывал все jlss, потому что у него около 12 вариантов - он не такой компактный, как rcsunco.


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

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

Если вы имеете дело с обязательными аргументами, чередующимися с аргументами опций (как до, так и после обязательных), у вас еще больше работы. Это используется многими системами VCS; CVS и GIT имеют возможность:

git -global option command [-sub option] [...]

Здесь вы запускаете один цикл getopts для получения глобальных опций; забрать обязательные аргументы; и запустите второй цикл getopts, чтобы получить подпараметры (и, возможно, запустить конечный цикл над аргументами "имя файла" ).

Не радость жизни?

Ответ 4

И я услышал совершенно противоположную вещь, что вы не должны использовать getopt, но встроенный getopts.

Кросс-платформенный getopt для оболочки script

Никогда не используйте getopt (1). getopt не может обрабатывать строки с пустыми аргументами или аргументы со встроенным пробелом. Пожалуйста, забудьте, что это когда-либо существовала.

Оболочка POSIX (и другие) предлагает getopts, который безопасен в использовании вместо этого.