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
2番目に開発中なので定義を得ました。
$ . 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 ...
とは異なり、readonly
thatは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は私が使用するもう一つの機能です。ただエラーメッセージを印刷してからスクリプトを終了します。