Как правильно вернуть значения в функции сценария оболочки для крайних случаев? - PullRequest
0 голосов
/ 21 октября 2018
getAnimalFolder() {
    local animal=""
    if [[ ${ANIMAL} == "lion" ]]; then
        animal = "./animals/lion/"
    elif [[ ${ANIMAL} == "tiger" ]]; then
        animal = "./animals/tiger/"
    elif [[ ${ANIMAL} == "cheetah" ]]; then
        animal = "./animals/cheetah/"
    else
        echo "inavalid animal"
        exit 1`enter code here`
    fi
    echo $animal
}

result=$(getAnimalFolder)
cd ../result/age/

Если животное не является львом, тигром или гепардом, функция возвращает недопустимое животное и, следовательно, выдает ошибку «Нет такого файла или каталога», вместо этого мне нужно выполнить выход с кодом = 1. Поэтому я пошелдля второго варианта -

if [[ ${ANIMAL} != "lion" && ${ANIMAL} != "tiger" && ${ANIMAL} != "cheetah" ]]; then
    echo "Invalid animal"
    exit 1
fi
getAnimalFolder() {
        local animal=""
        if [[ ${ANIMAL} == "lion" ]]; then
            animal = "./animals/lion/"
        elif [[ ${ANIMAL} == "tiger" ]]; then
            animal = "./animals/tiger/"
        elif [[ ${ANIMAL} == "cheetah" ]]; then
            animal = "./animals/cheetah/"
        fi
        echo $animal
}
result=$(getAnimalFolder)
cd ../result/age/

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

Ответы [ 2 ]

0 голосов
/ 21 октября 2018

Поместите животных в массив:

#!/bin/bash
animals=(lion tiger cheetah)

getAnimalFolder() {
   local i
   for i in "${animals[@]}"; do
      if [ "$i" == "${1}" ] ; then
        animaldir="./animals/${1}"
        return 0
      fi
   done
   exit 1
}

read -rp "Give me an animal: " animalname
getAnimalFolder "${animalname}"

echo "Animaldir=${animaldir}"

РЕДАКТИРОВАТЬ: я не использовал конструкцию result=$(getAnimalFolder), предполагая, что ОП хочет использовать новый путь один раз.При необходимости функцию можно изменить на

    echo "./animals/${1}"

Когда функция вызывается с помощью result=$(getAnimalFolder), OP должен посмотреть на строку

cd ../result/age/

Является ли result фиксированнойпуть или он хочет использовать путь из функции: cd ../$‹

0 голосов
/ 21 октября 2018

Здесь есть ряд проблем;# 1 и # 3 - это те, которые непосредственно касаются вашего вопроса.

  1. Когда функция / команда / все, что может понадобиться для печати как обычного вывода (например, путь к каталогу животных), так ивывод ошибки / состояния (например, «inavalid animal»), он должен отправлять обычный вывод на стандартный вывод (также известный как stdout или FD # 1, по умолчанию), а вывод ошибки / состояния - на стандартную ошибку (также известный как stderr или FD # 2),как это:

    echo "Invalid animal" >&2    # This is sent to stderr
    
  2. Как правило, функции должны return вместо exit ing.Если функция выполняет exit, она выходит из всей оболочки, но в этом случае функция выполняется в подоболочке из-за $( ), поэтому она только завершает ее.Использование return позволяет избежать этого несоответствия.

  3. Когда функция / команда / что-либо может дать сбой, вы должны проверить ее состояние выхода;Есть несколько способов сделать это:

    if result=$(getAnimalFolder); then
        : # command succeeded!
    else
        echo "OMG it failed!" >&2
        exit 1
    fi
    

    или

    result=$(getAnimalFolder)
    if [ $? -ne 0 ]; then    # $? is the status of the last command
        echo "OMG it failed!" >&2
        exit 1
    fi
    

    или

    result=$(getAnimalFolder) || {
        echo "OMG it failed!" >&2
        exit 1
    }
    

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

  4. В общем случае функции должны принимать свои данныев качестве аргументов, а не через глобальные переменные.Таким образом, в функции вы будете ссылаться на $1 вместо $ANIMAL, и вы будете запускать функцию с чем-то вроде:

    result=$(getAnimalFolder "$ANIMAL")
    

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

shellcheck.net хорошо распознает многие изэти распространенные ошибки.Настоятельно рекомендуется.

Вот что я получаю со всеми исправлениями на месте:

#!/bin/bash

getAnimalFolder() {
    local animalname=$1
    local animaldir=""
    case "$animalname" in
        lion ) animaldir="./animals/lion/" ;;

        tiger ) animaldir="./animals/tiger/" ;;

        cheetah ) animaldir="./animals/cheetah/" ;;

        * )
            echo "Invalid animal: $animalname" >&2
            return 1 ;;
    esac
    echo "$animaldir"
}

read -p "Give me an animal: " animalname
result=$(getAnimalFolder "$animalname") || {
    exit 1 # Appropriate error message has already been printed
}
cd "../$result/age/" || {
    echo "Error changing directory to ../$result/age/ -- aborting" >&2
    exit 1
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...