如何在shell脚本函数中为边缘情况正确返回值?

时间:2018-10-21 03:46:28

标签: bash shell syntax

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 :(得分:2)

这里有很多问题; #1和#3是直接解决您问题的人。

  1. 当函数/命令/所有内容可能需要打印常规输出(例如,动物目录的路径)和错误/状态输出(例如,“无效动物”)时,应将常规输出发送到标准输出(aka stdout aka FD#1,默认设置),以及将错误/状态输出为标准错误(aka stderr aka FD#2),如下所示:

    echo "Invalid animal" >&2    # This is sent to stderr
    
  2. 通常,函数应该return而不是exit。如果某个函数执行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
}

答案 1 :(得分:1)

将动物放在一个阵列中

MERGE INTO table1 AS T
USING table2 AS S
  ON T.Name = S.Name
  AND T.Owner = S.Owner
WHEN MATCHED THEN
UPDATE 
  SET T.Age = S.Age, 
      T.Height = S.Height;

编辑: 我没有使用构造#!/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}" ,假设OP希望一次使用新路径。需要时可以将功能更改为

result=$(getAnimalFolder)

使用 echo "./animals/${1}" 调用该函数时,OP需要查看该行

result=$(getAnimalFolder)

cd ../result/age/ 的固定路径,还是他想使用函数中的路径:     cd ../$ {result} / age /