eval(동적 변수를 생성하는 데 사용됨)을 선언으로 바꾸면 빈 변수가 생성되는 이유는 무엇입니까?

eval(동적 변수를 생성하는 데 사용됨)을 선언으로 바꾸면 빈 변수가 생성되는 이유는 무엇입니까?

bash >5를 사용하여 변수에 지정된 아키텍처를 기반으로 변수에 다른 값을 할당해 보았습니다. 이 작업을 수행하기 위해 함수를 사용합니다. 이것은 완벽하게 작동합니다.

# arguments:
  variable name to assign,
  value for mac arch,
  value for pi arch

create_variable_for_arch() {
  if [ "$_run_for_arch" = "mac" ]; then
    eval $1=\$2
  else
    eval $1=\$3
  fi
}

그러나 어떤 이유로 인해 내 스크립트가 중단되었습니다.

create_variable_for_arch() {
  if [ "$_run_for_arch" = "mac" ]; then
    declare "$1"="$2"
  else
    declare "$1"="$3"
  fi
}

다음은 create_variable_from_arch() 사용 방법을 보여주는 스니펫입니다.

declare _moonlight_opt_audio
declare _arch_specific_stream_command

#
while getopts "b:fahdr:s" options; do
  case $options in
    a)
      create_variable_for_arch "_moonlight_opt_audio" \
        "--audio-on-host" "-localaudio"
      ;;
  esac
done

create_variable_for_arch "_moonlight_opt_fps" "--fps 60" "-fps 60"

start_streaming() {
  _arch_specific_options="$_moonlight_opt_resolution $_moonlight_opt_fps $_moonlight_opt_audio $_moonlight_opt_display_type $_moonlight_opt_bitrate"
  create_variable_for_arch "_arch_specific_stream_command" "$_arch_specific_options stream $_target_computer_ip $_moonlight_opt_app_name" "stream $_arch_specific_options -app $_moonlight_opt_app_name $_target_computer_ip"

  moonlight $_arch_specific_stream_command
}

eval()을 사용할 때의 추적은 다음과 같습니다.

+ start_streaming
+ _arch_specific_options='--resolution 1920x1080 --fps 60   --bitrate 5000'
+ create_variable_for_arch _arch_specific_stream_command '--resolution 1920x1080 --fps 60   --bitrate 5000 stream 192.168.1.30 StreamMouse' 'stream --resolution 1920x1080 --fps 60   --bitrate 5000 -app StreamMouse 192.168.1.30'
+ '[' mac = mac ']'
+ eval '_arch_specific_stream_command=$2'
++ _arch_specific_stream_command='--resolution 1920x1080 --fps 60   --bitrate 5000 stream 192.168.1.30 StreamMouse'
+ moonlight --resolution 1920x1080 --fps 60 --bitrate 5000 stream 192.168.1.30 StreamMouse
moonlight --resolution 1920x1080 --fps 60 --bitrate 5000 stream 192.168.1.30 StreamMouse

그러나 선언에 따르면 다음과 같습니다.

    + start_streaming
    + _arch_specific_options=
    + create_variable_for_arch _arch_specific_stream_command ' stream 192.168.1.30 ' 'stream  -app  192.168.1.30'
    + '[' mac = mac ']'
    + declare '_arch_specific_stream_command= stream 192.168.1.30 '
    + echo moonlight
    moonlight

$_arch_특이적_options는 궁극적으로 가치가 없습니다. 무슨 일이야? 변수를 인용하거나 인용하지 않는 몇 가지 다른 방법을 시도했지만 인용이 무엇을 의미하는지 실제로 이해하지 못합니다.

답변1

declare( typeset다른 셸과 유사하며 bash의 별칭 으로도 알려져 있음 declare)은 현재 범위에서 변수를 선언합니다(유형 및/또는 값을 설정할 수 있음).

따라서 여기서는 함수의 지역 변수를 선언합니다 create_variable_for_arch. 함수가 반환되면 변수가 사라집니다.

bashdeclare/에는 변수를 선언하는 옵션이 typeset있습니다.-g글로벌), 그러나 함수 호출자의 범위가 아닌 가장 바깥쪽 범위에서 변수를 선언하고 해당 유형 및/또는 값을 설정하기 때문에 사용할 수 없으므로 거기에서는 꽤 쓸모가 없습니다 .mksh 건너뛰기만zshyash현지화해또는 정적 범위와 함께 ksh93을 사용하십시오.`declare name`과 `declare -g`는 무엇을 합니까?더 알아보기).

따라서 여기서는 evalnameref를 사용하거나 사용하는 옵션이 있습니다.

create_variable_for_arch() {
  if [ "$_run_for_arch" = mac ]; then
    eval "$1=\$2"
  else
    eval "$1=\$3"
  fi
}

또는 $_run_for_arch스크립트에서 상수를 가정합니다.

if [ "$_run_for_arch" = "mac" ]; then
  create_variable_for_arch() { eval "$1=\$2"; }
else
  create_variable_for_arch() { eval "$1=\$3"; }
fi

또는 nameref를 사용하십시오.

create_variable_for_arch() {
  typeset -n _var_name="$1"
  if [ "$_run_for_arch" = mac ]; then
    _var_name=$2
  else
    _var_name=$3
  fi
}

eval일반적으로 보안상의 이유로 이를 피하는 것이 (올바로) 권장되지만 eval올바르게 사용하면 안전합니다. 둘 다 코드를 평가할 수 있으므로 잘못 사용하면 안전 declare하지 않습니다.namerefs

모두:

f() { eval "$1=\$2"; }
f() { declare "$1=$2"; }
f() { declare -n v="$1"; v=$2; }

reboot이 명령은 다음과 같이 호출되면 실행됩니다.

f 'a[$(reboot)]' value

임의의 명령 실행 취약점을 방지하려면 첫 번째 인수가 변수 이름인지 확인하는 것이 중요합니다.

f() { declare $1=$2; }

상황은 더욱 악화될 것입니다. 이러한 매개변수 확장은 인용되지 않았기 때문에 분할+글로브의 적용을 받습니다. 따라서 내용조차도 $2다음과 같이 쉘 코드로 평가될 수 있습니다.

f var 'foo a[$(reboot)]='

관련 정보