【问题标题】:Right way to order functions and conditions订购功能和条件的正确方法
【发布时间】:2019-08-31 05:55:00
【问题描述】:

我正在编写一个 bash 脚本来配置 NFS 服务器。我能够手动配置 NFS 服务器并且它功能齐全。 现在我想在 bash 脚本中重现相同的步骤。 我有所有的步骤,每一步,shell 命令,我都放入了一个 bash 函数。 然后我有一个主要部分,我在其中进行函数调用,总共有 16 个函数。 在主要部分我想检查函数返回值,通常只是“$?”价值

if [ "function_1" -eq "0" ]; then
    echo "Success"
else
    echo "Failure"
fi

由于有16个函数调用,其中每个函数调用都依赖于前一个函数才能成功,所以整个事情变得有点笨拙

if [ "function_1" -eq "0" ]; then
    if [ "function_2" -eq "0" ]; then
        if [ "function_3" -eq "0" ]; then
            if [ "function_..." -eq "0" ]; then
                if [ "function_16" -eq "0" ]; then

我的问题是,这是构建程序的正确方法还是完全不使用函数而只在主要部分中进行命令验证会更好

chmod -R 0755 $SHARED_FILE_SYSTEM
if [ "$?" -eq "0" ]; then
    echo "Success"
else
    echo "Failure
fi

【问题讨论】:

  • 我假设你只是想要if function_1 && function_2 && ... && function 16; then
  • 嗨@chepner,谢谢你的信息。是的,我想过。我假设一旦一个函数条件失败,比如在 function_2 上,整个过程就会停止并返回 anon ")" 值。我怎么知道哪个函数调用失败了。我想向 asn 安装日志输出一条错误消息,以便用户可以检查哪个语句/命令导致错误
  • @user2872898,这是添加一些工具的问题。让每个函数在启动时设置一个变量,然后你就在那里——该值告诉你最后一个运行的变量……当然,如果你不只是想记录整个事情;对于我自己的安装程序,我运行set -x 并带有适当的信息PS4
  • 顺便说一句,请注意使用$? 通常是一种不好的做法,如果您要做的只是区分零/非零以便立即分支。 if chmod -R 0755 "$SHARED_FILE_SYSTEM"; then echo "Success"; else echo "Failure" 确保分支是基于 chmod 完成的,并且不会被记录命令或其他可能无意更改 $? 的事情中断。
  • ...我也,作为一个成语,做类似chmod 0755 "$SHARED_FILE_SYSTEM" || die "Failure setting permissions for shared filesystem的事情,而die是类似die() { echo "$*" >&2; exit 1; }的事情

标签: bash function conditional-statements


【解决方案1】:

一种选择是列出函数并在循环中运行它们,如Shellcheck-clean 代码所示:

#! /bin/bash -p

# ...

config_functions=(
    function_1
    function_2
    # ...
    function_15
    function_16 )

for cfunc in "${config_functions[@]}" ; do
    if "$cfunc" ; then
        printf 'Success: %s\n' "$cfunc"
    else
        printf 'Failure: %s (%d)\n' "$cfunc" "$?"
    fi
done
  • 无需明确检查$?,如果您这样做,Shellcheck 会发出警告。见Shellcheck SC2181
  • 如果您想在任何功能失败时立即停止,请酌情在if 的“失败”分支中添加breakexit "$?"

如果函数接受参数,这种方法也很有效,但前提是参数总是相同的。 (只需将参数添加到循环中的函数调用。)如果不同的函数采用不同的参数,则效率较低。您可以尝试将完整的函数调用放入数组中的字符串中,但处理需要引用的参数会很棘手。

考虑使用ChefPuppetAnsibleSalt 等配置管理工具来配置您的 NFS 服务。与脚本相比,它们有很多优势。

【讨论】:

  • 谢谢@pjh,这是很好的信息。这个shellcheck,循环函数,是否也适用于将输入传递给函数。 ``` function check_user () { getent passdw $1 check_user $USER_NAME```,在for循环中添加thos参数?是的,我很熟悉并且使用过其中一些构建/部署基础设施。那些需要有一个环境来设置它,即。厨师,安西布尔。我们已经有了一个带有脚本组件的构建/部署基础设施工具,我可以在其中添加一个脚本,该工具将在它部署到的服务器上执行该脚本。
  • @user2872898,我添加了一段关于将这种方法与带参数的函数一起使用的段落。简而言之:它可能不合适。请注意,Shellcheck 是用于 shell 代码的静态分析工具,而不是在 shell 代码中使用或完成的东西。它捕获了shell代码中的很多问题,并有助于提高代码质量。
【解决方案2】:

如果函数接受参数(并且不同的函数接受不同的参数),简化命令验证的一种方法是使用函数来完成:

#! /bin/bash -p

# ...

# Run a configuration command (passed as arguments) and report if it succeeds or
# fails.  Call 'exit' if the command fails.
function doconf
{
    if "$@" ; then
        printf 'Success: %s\n' "$*"
        return 0
    else
        local status=$?
        printf 'Failure: %s (%d)\n' "$*" "$status"
        exit "$status"
    fi
}

doconf function_1 arg1
doconf function_2 arg2a arg2b
# ...
doconf function_15 arg15a arg15b arg15c arg15d
doconf function_16 arg16a arg16b arg16c

如果您不希望程序在函数失败时立即退出,您可以通过将doconf 调用放入子shell 中来捕获doconf 完成的exit

# ...

(
    doconf function_1 arg1
    doconf function_2 arg2a arg2b
    # ...
    doconf function_15 arg15a arg15b arg15c arg15d
    doconf function_16 arg16a arg16b arg16c
)

# Program execution continues here, even if a `doconf` exits
# ...
  • doconf 不是一个好名字。您可能想要更改它。
  • doconf 应该检查以确保提供了至少一个参数(命令名称)。

【讨论】:

    【解决方案3】:

    检查$? 是一种反模式。最好直接在if 语句中测试命令:

    if cmd; then
        ...
    fi
    

    有明确的if 检查和错误消息是笨拙的。避免它的常见模式是在命令失败时简单地退出,并依靠命令打印自己的错误消息。你不需要自己打印任何东西。然后,您可以在命令失败时使用|| return|| exit 退出,其中a || bif ! a; then b; fi 的简写。

    function_1 || exit
    function_2 || exit
    function_3 || exit
    ...
    

    请注意,returnexit 将自动使用 $?

    如果您想在任何命令失败时退出,更简单的选择是使用set -e 启用errexit 标志。这样做,shell 将自动退出脚本,而无需插入繁琐的检查。

    set -e
    
    function_1
    function_2
    function_3
    ...
    

    【讨论】:

      猜你喜欢
      • 2016-02-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-07-03
      相关资源
      最近更新 更多