Как использовать ${OPTARG} на getopts?

У меня есть следующий код:

while getopts ":p:t:n:" o; do
case "${o}" in
    p)
        p=${OPTARG}
        numep=$p
        mkdir $numep
        ;;
    t)
        t=${OPTARG}
        tip=$t
        if [ $tip == "c" ]; then
            touch $numep/$numep.c
            touch $numep/$numep.h
        elif [ $tip == "c++" ]; then
            touch $numep/$numep.cpp
            touch $numep/$numep.h
        fi
            ;;
        n)
            n=${OPTARG}
            nrFis=$n
            if [ $tip == "c" ]; then
                for i in $(seq $n); do
                    touch $numep/src/$numep$i.c
                    touch $numep/inc/$numep$i.h
                done
            elif [ $tip == "c++" ]; then
                for i in $(seq $n); do
                    touch $numep/src/$numep$i.cpp
                    touch $numep/inc/$numep$i.h
                done
            fi
            ;;
        *)
            err-help
            ;;
        esac
    done

err-help — это предварительно определенная функция, которая предоставляет инструкции для правильного вызова.

Правильный вызов скрипта выглядит так: ./script.sh -p project_name -t project_type -n source_files_number

Что я пытаюсь сделать: создать каталог с определенным количеством исходных файлов для проекта c или c++.

Мои вопросы здесь: Как мне использовать ${OPTARG}? Что именно делает p=${OPTARG}? Будут ли переменные p, t, n заполнены правильным содержимым? Как я делаю это правильно, пожалуйста? :(


person Marko    schedule 27.03.2016    source источник
comment
Там существует много руководства о getopts. С чем именно у вас проблемы?   -  person Andrea Corbellini    schedule 27.03.2016
comment
Как я и сказал. Я проанализировал много туториалов и это то, что я мог бы написать после. Если я напишу для каждого случая p=${OPTARG}, t=${OPTARG}, n=${OPTARG}. Будут ли эти переменные содержать правильные данные? Или все будут иметь одинаковый контент? Я не знаю здесь подвоха...   -  person Marko    schedule 27.03.2016
comment
Да, они будут содержать правильные данные.   -  person Andrea Corbellini    schedule 27.03.2016


Ответы (1)


Вы используете getopts правильно, но делаете предположения о том, в каком порядке вы увидите варианты. Подробнее об этом позже.

Давайте посмотрим на пару отрывков из help getopts

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

... Когда опция требует аргумента, getopts помещает этот аргумент в переменную оболочки OPTARG.

Это магия getopts. Поскольку вы добавили p: в свою строку опций, когда $0 = "p", переменная $OPTARG будет содержать все, что вы предоставили -p, в данном случае строку "project_name".

Теперь немного обзора кода:

case "${o}" in
    p)
        p=${OPTARG}
        numep=$p
        mkdir $numep
        ;;

Вам нужно использовать фигурные скобки только в том случае, если вы используете массивы, или вам нужно так устранить неоднозначность имени переменной из окружающего текста ($o_x против ${o}_x). Было бы более аккуратно написать

case "$o" in ...

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

numep="$OPTARG"

Всегда цитируйте свои переменные. Всегда. Если вы специально не знаете, когда оставить их без кавычек. Если я укажу -p "some dir with spaces", то mkdir $numep создаст 4 каталога. Вам нужно написать

mkdir "$numep"

Если каталог $numep уже существует, вы получите сообщение об ошибке. Если вы этого не хотите, используйте mkdir -p "$numep" (см. man mkdir)

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

./script.sh -t project_type -n source_files_number -p project_name

Анализ вашего параметра предполагает, что в ветвях t и n вы уже установили $numep, поэтому вы предполагаете, что пользователь предоставил -p сначала. Если первым идет -t, то $numep пуст, и вы попытаетесь создать файл /.h. Даже если у вас есть разрешение на запись в корневой каталог, это не то, что вы хотите делать. Проанализируйте все варианты, прежде чем начинать что-то делать.

Я бы написал это:

while getopts ":p:t:n:" o; do
    case "$o" in
        p)  numep="$OPTARG" ;;
        t)  case "$OPTARG" in
                c)  ext="c" ;;
                "c++")  ext="cpp" ;;
                *)  echo "Error: use 'c' or 'c++' for -t option" >&2
                    exit 1
                    ;;
            esac
            ;;
        n)  nrFis="$OPTARG" ;;
        *)  err-help ;;
    esac
done

mkdir -p "$numep"
touch "$numep/$numep.$ext"
touch "$numep/$numep.h"
for ((i=1; i<=$nrFis; i++)); do
    touch "$numep/src/$numep$i.$ext"
    touch "$numep/inc/$numep$i.h"
done
person glenn jackman    schedule 27.03.2016
comment
Да, вы правы насчет порядка -p -t -n. Я передам ваш код позже и вернусь с моими вопросами или результатами. Спасибо за ваше время, Гленн! :) - person Marko; 27.03.2016