변수를 조건부로 읽기 전용으로 설정하는 기능

변수를 조건부로 읽기 전용으로 설정하는 기능

errexit다른 안전하지 않은 작업으로 인해 읽기 전용 변수를 홀수로 설정하는 스크립트가 있는 경우 :

#!/bin/bash
set -e 
declare -r NOTIFY=$(case "$OS" in (macosx) echo macos_notify ;; (linux) echo linux_notify ;; (*) echo : ;; esac)
declare -r SAY=_say # _say is a function
declare -r VERSION=0.99
set +e 

두 번째로 개발 중이기 때문에 정의를 얻었습니다.

$ . s.bash 

$ . s.bash 
bash: declare: NOTIFY: readonly variable
Exited

일반적으로 declare -r EXISTING_VAR스크립트를 중지하거나 오래된 작업 정의를 삭제하지 않습니다 EXISTING_VAR.

그러나 의 경우 errexit기존 변수에 대한 할당이 당연히 실패합니다. 쉬운 옵션은 스크립트의 해당 부분을 제거 -r하거나 사용하는 것입니다.set +e

그 외에도,declare -r이름이 이미 존재하는 경우 대체 하지만 재할당할 수 없는 Bash 함수를 작성할 수 있습니까??

나는 시도했다:

# arg #1: var name, #2: value
set_var_once () {
  # test whether the variable with the 
  # name stored in $1 exists 
  if [[ -z "${!1}" ]] 
  then # if it doesn't, set it
    declare -r $1=$2
  fi
}

비슷한 것을 시도했는데 여기 어딘가에 필요할 것 eval "declare -r $1=$(eval $2)"같은 느낌이 들었지만 eval어디에 있는지 잘 모르겠습니다.

모든 버전 set_var_once에서는 변수가 있어야 할 위치에 설정되지 않습니다.

답변1

declare -r변수를 읽기 전용으로 만들 뿐만 아니라주장하다현재 범위에 있으므로 현재 함수에 로컬로 만듭니다. 당신이 원하는 것은 readonly전자입니다.

readonly_once() {
  local __assign
  for __assign do
    [[ -v ${__assign%%=*} ]] || readonly "$__assign"
  done
}

다음과 같이 사용됩니다:

readonly_once VAR1=foo VAR2="$(cmd)" PATH ...

와 달리 readonlythat은 readonly_once키워드가 아닙니다(예 readonly,반품키워드는 bash이 사실을 숨기지만) $(cmd)분할+글로브를 방지하기 위해 인용해야 하며, 이 경우 할당이 아닙니다.

$(cmd)cmd값이 결국 할당되지 않더라도( VAR2정의된 경우) 확장됩니다(따라서 실행됩니다).

이 함수는 배열이나 연관 배열이 아닌 스칼라 변수에서만 작동합니다.

답변2

쉘이 bash인 경우 다음을 사용할 수 있습니다 -v test.

[[ -v NOTIFY ]]  || NOTIFY=$(case "$OS" in (macosx) echo macos_notify ;; (linux) echo linux_notify ;; (*) echo : ;; esac)
[[ -v SAY ]]     || SAY=_say # _say is a function
[[ -v VERSION ]] || VERSION=0.99

예를 들어

$ unset myvar
$ [[ -v myvar ]] && echo "already set to $myvar" || myvar=10
$ [[ -v myvar ]] && echo "already set to $myvar" || myvar=10
already set to 10
$ myvar=5
$ [[ -v myvar ]] && echo "already set to $myvar" || myvar=10
already set to 5
$ myvar=""
$ [[ -v myvar ]] && echo "already set to $myvar" || myvar=10
already set to 

또는 ${param:=value}확장 및 :명령을 사용하십시오.

: ${NOTIFY:=$(case "$OS" in (macosx) echo macos_notify ;; (linux) echo linux_notify ;; (*) echo : ;; esac)}
: ${SAY:=_say}
: ${VERSION:=0.99}

입증하다:

$ OS=macosx
$ echo "$NOTIFY"

$ : ${NOTIFY:=$(case "$OS" in (macosx) echo macos_notify ;; (linux) echo linux_notify ;; (*) echo : ;; esac)}
$ echo "$NOTIFY"
macos_notify
$ NOTIFY=no
$ : ${NOTIFY:=$(case "$OS" in (macosx) echo macos_notify ;; (linux) echo linux_notify ;; (*) echo : ;; esac)}
$ echo "$NOTIFY"
no
$ NOTIFY=""
$ : ${NOTIFY:=$(case "$OS" in (macosx) echo macos_notify ;; (linux) echo linux_notify ;; (*) echo : ;; esac)}
$ echo "$NOTIFY"
macos_notify

답변3

다음이 유용하다고 생각했습니다.

# Function SetCommand
# Function to find a command and assign the absolute path of that command to
# a variable.  The intent is to only invoke known good commands.
# If a command is not found, abort.  Assume the script needed this command.
# Function is called as follows:
#     SetCommand assignmentVariableName queryString
#
# where
#     assignmentVariableName is the name of the variable to which the path
# is assigned,
#     queryString is the name of the command
#
# Example: SetCommand CMD_FOO foo
#
SetCommand() {
    local _assignmentVariableName
    local _fullPath
    local _queryString

    [ $# -ne 2 ] && AbortScript "${FUNCNAME}: Invalid number of arguments."

    _assignmentVariableName="$1"
    [[ "" == "${_assignmentVariableName}" ]] && AbortScript "${FUNCNAME}: assignmentVariableName is blank."
    shift

    _queryString="$1"
    [[ "" == "${_queryString}" ]] && AbortScript "${FUNCNAME}: queryString is blank."
    shift

    if [[ ! -z ${!_assignmentVariableName+x} ]]; then
        Print2Stderr "${FUNCNAME}: ${_assignmentVariableName} already defined."
        return ${constErrorExitCode}
    fi

    _fullPath=$(${CMD_WHICH} ${_queryString} 2>/dev/null)
    [[ "" == "${_fullPath}" ]] && AbortScript "${FUNCNAME}: Could not find command, ${_queryString}."

    eval readonly ${_assignmentVariableName}=${_fullPath}

    return ${constSuccessExitCode}
} # End SetCommand

AbortScript는 제가 사용하는 또 다른 기능입니다. 단지 오류 메시지를 인쇄한 다음 스크립트를 종료합니다.

관련 정보