전역 읽기 전용 변수와 이름이 같은 지역 변수를 가질 수 없습니다.

전역 읽기 전용 변수와 이름이 같은 지역 변수를 가질 수 없습니다.
#!/bin/bash

readonly x=2

function test {
 local x=1
 echo ${x}
}

test
echo $x

밝혀지다,

readonly-local-test.sh: line 6: local: x: readonly variable
2
2

이는 변수가 읽기 전용일 때 발생합니다. 하지만 아래와 같이 읽기 전용 제한을 제거하면

#!/bin/bash

x=2
function test {
 local x=1
 echo ${x}
}

test
echo $x

밝혀지다,

1
2

읽기 전용 전역 변수를 숨길 수 없는 이유는 무엇입니까?

답변1

Bash는 섀도우를 할 수 없는 것 같습니다.글로벌읽기 전용 변수입니다. 그러나 나는 이것이 Posix 추가라고 생각합니다 $@.현지의읽기 전용 변수는 숨길 수 있습니다. Posix에는 지역 변수가 없기 때문에 모든 동작은 posix 일관성을 유지합니다.

readonly항상 전역 변수를 정의하십시오. 로컬 읽기 전용 변수는 local -r함수 내부에서 사용하거나 정의할 수 있습니다 declare -r( local함수 내부처럼 동작함).

모든 전역 변수를 지역 변수*로 변환하도록 스크립트를 수정할 수 있습니다. 예를 들어, 모든 것을 내부에 포장 main() { YOUR_SCRIPT_HERE; }; main하고 모두 readonly다음으로 바꿉니다 local -r.

#! /usr/bin/env bash
main() {
  local -r x=constant
  declare -p x
  f() {
    local x=mutable1
    declare -p x
    x=mutable2
    declare -p x
  }
  f
  declare -p x
  # x=... would not work here
}
main

물론 순진한 수정보다는"할 것이다모두main" 내부 스크립트더 나은 구조를 위해 외부에서 함수를 정의할 수도 있습니다 main.

#! /usr/bin/env bash
main() {
  local -r x=constant
  declare -p x
  f
  declare -p x
  # x=... would not work here
}
f() {
  local x=mutable1
  declare -p x
  x=mutable2
  declare -p x
}
main

* 두 버전 모두에서 (이전에는 전역 변수였던) main가 호출될 때 내부 범위에 있다는 점에 유의하세요. x따라서 내부 지역 변수는 대부분 실제 전역 변수처럼 동작합니다. 당신이 좋아하는 경우.fmainmainC예:

g() { local x=1; h; echo "g.x = $x"; }
h() { x=2; }
g
echo "global.x = $x";

… 인쇄 중

g.x = 2
global.x =

답변2

내가 아는 한, 이 동작은 "디자인으로서의 작업"이라고 명명될 수 있습니다. 읽기 전용 변수가 있고 해당 변수의 값을 변경할 수 없습니다. Bash에서는 이러한 전역 변수 세트를 재정의하는 변수를 정의하는 옵션이 없습니다.

답변3

OP와 같은 어떤 경우에는 확실히 짜증나는 행동이지만, 이해합니다.이것은 훌륭한 기능입니다. 고려하다전역 읽기 전용변수는 이래야 합니다. 일단 설정되면,어디에나이 변수에는 스크립트에 알려진 값이 있습니다. 여기에는 소스 파일에서 가져온 모든 기능은 물론 모든 깊이의 기능이 포함됩니다.

이로 인해 설명된 충돌이 발생할 수 있지만 이를 완화하는 "일반적인" 방법은 명명 규칙을 사용하여 두 유형( ALL_CAPS_READONLY_GLOBALvs. snake_case_local또는 ) 을 구별하는 것입니다 camelCaseLocal.

쓰기 가능한 전역 변수진짜 문제입니다. 나는 "특별"하고 부주의하게 수정해서는 안 된다는 것을 나타내기 위해 앞에 "_"(밑줄)을 사용하는 것을 좋아합니다. 사실, 나는 보통 다음과 같이 그러한 값에 대해 getter/setter 함수를 제공하는 것을 선호합니다.

declare -g _GLOBAL_FOO="default-value"

fooGet() { printf "%s"  "${_GLOBAL_FOO}"; }
fooSet() { _GLOBAL_FOO="${1:-}"; }

배열의 경우 더 복잡하지만 아이디어는 인덱스 유형과 연관 유형으로 확장될 수 있습니다. 또한 위에 표시된 작업을 실제로 수행하지 않고 fooGet()대신 관심 있는 값을 받기 위해 변수 이름을 제공합니다. 그러므로

local myVar=$(fooGet)             ## okay, but...
local myVar; fooGet --var myVar   ## nicer
fooGet --var myVar                ## works, sort-of, but avoid this!!

실제로 두 가지 fooGet방법 모두 단일 기능에서 잘 작동합니다. --var VARNAME인수가 제공되면 값은 nameref를 통해 설정됩니다 . 그렇지 않으면 지원 양식의 값이 출력됩니다 $(foo).

또한 초기화/구성/시작 중에 전역적으로 쓰기가 가능해야 하는 경우 다음과 같은 다른 기능을 추가하세요.

fooFreeze() { declare -ga _GLOBAL_FOO; }

"잠금"(현재 값을 유지하면서 읽기 전용으로 설정) 이렇게 하면 처음에는 유연성이 허용되고 초기화가 완료되면 값을 변경할 수 없게(및 재정의할 수 없게) 만듭니다.

관련 정보