선언이 사용되는 경우 Bash 변수는 배열 내에서 확장되지 않습니다.

선언이 사용되는 경우 Bash 변수는 배열 내에서 확장되지 않습니다.

최근에 나는 bash 내장에 대해 더 많이 읽기로 결정했고 이로 인해 다음 declare과 같은 결과를 얻었습니다.localreadonly

local variable_name
variable_name='value'
readonly variable_name

도착하다:

variable_name='value'
declare -r variable_name

이 변경으로 작성해야 할 줄 수가 줄어들고 변수 값이 정수임을 bash에 알리는 것과 같은 일부 속성을 설정할 수 있게 되었습니다. 그러나 cURL 별칭으로 사용할 함수를 만드는 동안 배열 내부의 변수는 사용하면 확장되지 않지만 declare및 를 사용하면 잘 확장되는 것을 발견했습니다.localreadonly

예는 다음과 같습니다.

#!/usr/bin/env bash

set -o errexit -o errtrace -o pipefail -o nounset
IFS=$'\n\t'

curl() {

  curl_version="$(command curl --version | awk 'NR==1 {print $2}')"
  declare -r curl_version

  curl_args=(
    --user-agent "curl/${curl_version}"
    --silent
    --fail
  )

  command curl "${curl_args[@]}" \
    "${@}"

}

curl --url 'https://httpbin.org/get'

변수가 어떤 이유로든 확장되지 않기 때문에 --user-agent배열 부분은 bash가 아는 한 바인딩되지 않은 변수이고 set -o nounset.

며칠 동안 작동시키려고 노력했기 때문에 이제 수건을 던지고 도움을 받아야 할 때라고 생각했습니다. 내가 뭘 잘못하고 있는지 올바른 방향으로 알려줄 수 있는 사람이 있나요?

편집하다:

언급하는 것을 잊어버렸지만, 예를 들어 같은 줄에 변수를 선언하면 declare -r variable_name문제가 발생합니다.쉘체크 SC2155, 그래서 값을 설정한 후 선언하려고 합니다.

답변1

그리고:

curl_version="$(command curl --version | awk 'NR==1 {print $2}')"
declare -r curl_version

함수에서 $curl_version전역 변수를 일부 값으로 설정한 다음 처음에 설정되지 않은 별도의 로컬 읽기 전용 변수를 만듭니다.

당신이 원하는 것 같습니다 :

# instantiate a new local variable (but in bash it inherits the "export"
# attribute if any of the variable with same name in the parent scope)
local curl_version

# unset to remove that export attribute if any. Though you could
# also change the above to local +x curl_version
unset -v curl_version

# give a value:
curl_version="$(command curl --version | awk 'NR==1 {print $2}')"

# make that local variable read only
local -r curl_version

( 변수를 로컬로 만들고 싶다는 점을 더 명확하게 하기 local위해 여기서는 not을 사용했습니다 .)declare

또는 동시에 모든 작업을 수행합니다.

local +x -r curl_version="$(command curl --version | awk '{print $2; exit}')"

(shellcheck가 지적한 것처럼, 그러면 파이프²의 종료 상태가 손실됩니다.)

어쨌든 저는 C에서와 같이 셸에서 /를 사용하지 않을 것입니다. readonly특히 셸(ksh93 제외)에는 C와 같은 정적 범위가 없기 때문입니다. 그리고 (인스턴스와 반대로) 전역 범위에서 읽기 전용으로 설정된 경우 함수의 로컬 변수를 만들 수 없습니다.typeset -rconstbashbashzsh

예를 들어:

count() {
  local n
  for (( n = 0; n < $1; n++ )) { echo "$n"; }
}

readonly n=5
count "$n"

zsh에서는 작동하지만 bash에서는 작동하지 않습니다. 그냥 사용 하고 절대 사용하지 않으면 local -r괜찮을 것입니다 readonly.


어쨌든 typeset// declare에서는 local동일합니다 bash. 유일한 차이점은 local함수 외부에서 사용하려고 하면 오류를 보고한다는 것입니다. typeset -r및 사이의 차이점( readonly및 사이와 동일)은 함수 내에서 호출되는 경우 후자가 새 변수를 인스턴스화하지 않는다는 것입니다.typeset -xexport

² exit해당 버전에서 SIGPIPE로 종료할 수 있는 첫 번째 줄 이후 입력 처리를 중지하는 방법을 확인하세요(출력이 한 번 전송되고 파이프에 맞기 때문에 실제로는 불가능함). 그 이후로 파이프는 결국 141 종료로 실패할 수 있습니다. 상태이지만 변수에 값을 할당할 수 있는 한 자체적으로는 여전히 성공합니다.awkawkcurlcurlpipefaillocal

답변2

함수의 첫 번째 줄은글로벌바꾸다.

  curl_version="$(command curl --version | awk 'NR==1 {print $2}')"

함수의 두 번째 줄은읽기 전용, 비어 있음, 로컬바꾸다

  declare -r curl_version

이 지역 변수는 전역 변수의 값을 대체합니다.

다음 발췌 내용을 참고하세요 help declare.

함수 내에서 사용될 때 "declare"는 "local" 명령을 사용하는 것처럼 이름을 로컬로 만듭니다. '-g' 옵션은 이 동작을 억제합니다.

나는 이것을 추천한다:

curl() {

  local -r curl_version="$(command curl --version | awk 'NR==1 {print $2}')"

  local curl_args=(
    --user-agent "curl/${curl_version}"
    --silent
    --fail
  )

  command curl "${curl_args[@]}" \
    "${@}"
}

변수를 확인하려면 declare -p curl_version curl_args명령 호출 전에 함수에 변수를 추가하세요.

관련 정보