Seemingly semantic difference in global versus loc

2019-02-28 12:03发布

Given the following bash script:

#!/usr/bin/env bash

echo $BASH_VERSION

# local [option] [name[=value] ... | - ]
#   For each argument, a local variable named name is created, and assigned value.  The option can  be  any
#   of  the options accepted by declare.  When local is used within a function, it causes the variable name
#   to have a visible scope restricted to that function and its children.  If name is -, the set  of  shell
#   options  is  made  local to the function in which local is invoked: shell options changed using the set
#   builtin inside the function are restored to their original values when the function returns.   With  no
#   operands,  local  writes a list of local variables to the standard output.  It is an error to use local
#   when not within a function.  The return status is 0 unless local is used outside a function, an invalid
#   name is supplied, or name is a readonly variable.

function __count_words {
    local feedback=$(echo "app1 app2")
    echo $feedback
    return $(IFS=' ' set -- ${feedback} && echo $#)
}

function invoke_func {
    declare -f $1_${DF_SYSTEM} >/dev/null
    [ $? -eq 0 ] && { $1_${DF_SYSTEM}; return $?; } || { $1; return $?; }
}

function check_words {
    local list=$(invoke_func __count_words)
    local count=$?
    echo "V1: XXXX [c=$count] > $list"
    list2=$(invoke_func __count_words)
    local count2=$?
    echo "V2: XXXX [c=$count2] > $list2"
}

check_words

The output is:

4.4.12(1)-release
V1: XXXX [c=0] > app1 app2
V2: XXXX [c=2] > app1 app2

What is the rationale behind the decision to make a seemingly similar assignment syntax different with regard to return values? I reckon I found the relevant section of the bash(1) man page, however it is unclear as to why this was done that way.

In the case of the local variable assignment, the return status is always 0, however with the global assignment, the return status varies depending on the return status of the called function inside the assignment.

标签: bash shell
1条回答
Summer. ? 凉城
2楼-- · 2019-02-28 12:46

This is an issue that Shellcheck detects as SC2155; consider reading the linked wiki page (and, perhaps, running your code through Shellcheck before asking about it here in the future).


A best-practices implementation of your check_words function would look like this, and does not exhibit your issue:

check_words() {
    local list count count2               # intentionally leaving list2 global
    list=$(invoke_func __count_words)
    count=$?
    echo "V1: XXXX [c=$count] > $list"
    list2=$(invoke_func __count_words)
    count2=$?
    echo "V2: XXXX [c=$count2] > $list2"
}

The local keyword is a built-in command. Like other commands, it has an exit status. Thus, that exit status overrides any exit status from a command substitution used to derive a value.

(Also, the function keyword makes your code gratuitously incompatible with POSIX sh, while not adding any compensating advantages; it's best avoided in favor of POSIX-compliant function declaration syntax).

查看更多
登录 后发表回答