eval을 사용하여 bash의 변수에 공백을 포함하는 값을 할당하는 방법


19

를 사용하여 변수에 값을 동적으로 할당하고 싶습니다 eval. 다음 더미 예제가 작동합니다.

var_name="fruit"
var_value="orange"
eval $(echo $var_name=$var_value)
echo $fruit
orange

그러나 변수 값에 공백이 포함되어 eval있으면 $var_value큰 따옴표로 묶어 도 오류가 반환됩니다 .

var_name="fruit"
var_value="blue orange"
eval $(echo $var_name="$var_value")
bash: orange : command not found

이것을 우회하는 방법은 무엇입니까?

답변:


13

평가를 사용하지 말고 사용하십시오. declare

$ declare "$var_name=$var_value"
$ echo "fruit: >$fruit<"
fruit: >blue orange<

11

eval이것을 위해 사용하지 마십시오 . 사용하십시오 declare.

var_name="fruit"
var_value="blue orange"
declare "$var_name=$var_value"

단어 분리는 문제가되지 않습니다. 왜냐하면 그 다음에 오는 모든 것이 첫 단어 만이 아니라의 =값으로 취급 되기 때문 declare입니다.

에서 bash4.3라는 이름의 참고 문헌이 조금 더 간단합니다.

$ declare -n var_name=fruit
$ var_name="blue orange"
$ echo $fruit
blue orange

당신 eval 일을 수 있지만 여전히해서는 안됩니다 :) 사용 eval하는 것은 나쁜 습관입니다.

$ eval "$(printf "%q=%q" "$var_name" "$var_value")"

2
eval 그 방법을 사용 하는 것은 잘못입니다. $var_value전달하기 전에 확장 중이므로 eval쉘 코드로 해석됩니다! (예를 들어 시도 var_value="';:(){ :|:&};:'")
Stéphane Chazelas

1
좋은 지적; 안전하게 할당 할 수없는 문자열이 있습니다 eval(사용하지 말아야하는 이유 중 하나입니다 eval).
chepner

@ chepner-나는 그것이 사실이라고 생각하지 않습니다. 어쩌면 적어도이 것은 아닙니다. 매개 변수 대체는 조건부 확장을 허용하므로 대부분의 경우 안전한 값만 확장 할 수 있다고 생각합니다. 여전히 가장 큰 문제 $var_value는 인용을 뒤집는 것 중 하나입니다-안전한 가치를 가정 할 때 $var_name (실제로 위험한 가정 일 수 있습니다) 오른쪽 따옴표를 큰 따옴표로 묶어야합니다. 그 반대의 경우도 마찬가지입니다.
mikeserv

eval, 사용 printfbash특정 %q형식을 수정했다고 생각 합니다. 이것은 여전히 ​​사용하는 것이 좋지 eval않지만 이전보다 안전하다고 생각합니다. 작동하기 위해 많은 노력을 기울여야한다는 사실은 declare대신 참조를 사용 하거나 이름을 지정 해야한다는 증거입니다 .
chepner

글쎄, 실제로 제 생각에는 명명 된 참조가 문제입니다. 내 경험에 - - 사용하는 가장 좋은 방법은 같은 ...입니다 set -- a bunch of args; eval "process2 $(process1 "$@")"경우 process1단지 인쇄와 같은 번호를 인용 "${1}" "${8}" "${138}". '"${'$((i=$i+1))'}" '대부분의 경우 처럼 쉽고 간단 합니다. 색인화 된 참조는 안전하고 강력하며 빠릅니다 . 아직도-나는 upvoted했다.
mikeserv

4

작업하기에 좋은 방법 은 테스트 용 eval으로 교체하는 것입니다 echo. echo그리고 eval(우리는 따로 설정하면 같은 일을 \x일부 수행 확장 echo등의 구현 bash'어떤 조건들).

두 명령 모두 사이에 하나의 공백으로 인수를 결합합니다. 차이점은 결과를 쉘 코드로 평가 / 해석 하는 동안 결과 를 echo 표시 한다는 것입니다.eval

그래서 어떤 쉘 코드를 보려고

eval $(echo $var_name=$var_value)

평가하면 다음을 실행할 수 있습니다.

$ echo $(echo $var_name=$var_value)
fruit=blue orange

그것은 당신이 원하는 것이 아니라 원하는 것입니다.

fruit=$var_value

또한 $(echo ...)여기를 사용하는 것은 의미가 없습니다.

위를 출력하려면 다음을 실행하십시오.

$ echo "$var_name=\$var_value"
fruit=$var_value

따라서 해석하기 위해 간단히 다음과 같습니다.

eval "$var_name=\$var_value"

개별 배열 요소를 설정하는 데에도 사용할 수 있습니다.

var_name='myarray[23]'
var_value='something'
eval "$var_name=\$var_value"

다른 사람들이 말했듯이 코드를 bash구체적으로 신경 쓰지 않으면 다음 declare과 같이 사용할 수 있습니다 .

declare "$var_name=$var_value"

그러나 부작용이 있습니다.

변수의 범위를 변수가 실행되는 함수로 제한합니다. 따라서 다음과 같은 경우에는 변수를 사용할 수 없습니다.

setvar() {
  var_name=$1 var_value=$2
  declare "$var_name=$var_value"
}
setvar foo bar

그것이 foo지역 변수를 선언 setvar하므로 쓸모가 없습니다.

bash-4.2전역 변수 를 선언 하는 -g옵션을 추가 했지만 호출자가 함수 인 경우 호출자의 변수와 달리 전역 변수를 설정하기 때문에 원하는 것이 아닙니다 .declaresetvar

setvar() {
  var_name=$1 var_value=$2
  declare -g "$var_name=$var_value"
}
foo() {
  local myvar
  setvar myvar 'some value'
  echo "1: $myvar"
}
foo
echo "2: $myvar"

어떤 출력 :

1:
2: some value

또한 변수가 이미 설정된 경우 declare호출 되는 동안 declare(실제로 bashKorn 쉘의 typeset내장 에서 개념을 빌린 ) declare새 변수를 선언하지 않으며 할당 방법은 변수 유형에 따라 다릅니다.

예를 들어 :

varname=foo
varvalue='([PATH=1000]=something)'
declare "$varname=$varvalue"

varname이전에 스칼라 , 배열 또는 연관 배열 로 선언 된 경우 다른 결과가 생성 될 수 있습니다 (그리고 잠재적으로 불쾌한 부작용이 발생할 수 있음) .


2
배쉬 고유의 문제는 무엇입니까? OP는 bash 태그를 질문에 넣었으므로 bash를 사용하고 있습니다. 대체 제품을 제공하는 것은 좋지만, 이식성이 없기 때문에 누군가에게 쉘의 기능을 사용하지 말라고 말하는 것은 어리석은 일입니다.
Patrick

@ 패트릭, 스마일리 본? 그러나 이식 가능한 구문을 사용하면 코드를 bash사용할 수없는 다른 시스템으로 코드를 이식 해야하거나 더 나은 / 빠른 셸이 필요하다는 것을 알면 노력이 줄어 듭니다 . 이 eval구문은 모든 Bourne과 같은 셸에서 작동하며 POSIX이므로 모든 시스템이 sh작동 하는 위치를 갖습니다 . (또한 내 대답이 모든 껍질에 적용된다는 것을 의미합니다. 조만간 여기에서 발생하는 것처럼 bash가 아닌 특정 질문 이이 질문의 복제본으로 닫히는 것을 볼 수 있습니다.
Stéphane Chazelas

그러나 $var_name토큰 이 포함되어 있다면 어떨까요? ...처럼; ?
mikeserv

@ mikeserv, 변수 이름이 아닙니다. 콘텐츠를 신뢰할 수없는 경우 ( evaldeclarePATH , TMOUT, PS4, SECONDS...).
Stéphane Chazelas

그러나 첫 번째는 항상 변수 확장이며 두 번째까지는 변수 이름이 아닙니다. 내 대답에 나는 매개 변수 확장으로 그것을 살균하지만, 첫 번째 단계에서 서브 쉘에서 살균을 암시하는 경우 이식 가능하게 w / 할 수 있습니다 export. 나는 끝에 괄호 비트를 따르지 않습니다.
mikeserv

1

당신이 할 경우 :

eval "$name=\$val"

... 그리고 쉘이 간단한 명령을 구분하는 것으로 해석 할 수 $name있는 ;-또는 다른 여러 토큰을 포함합니다 -적절한 쉘 구문이 앞에옵니다.

name='echo hi;varname' val='be careful with eval'
eval "$name=\$val" && echo "$varname"

산출

hi
be careful with eval

그러나 때때로 그러한 진술의 평가와 실행을 분리하는 것이 가능할 수 있습니다. 예를 들어 alias명령을 사전 평가하는 데 사용할 수 있습니다. 다음 예에서 변수 정의 는 평가 alias중인 $nm변수에 ASCII 영숫자 또는와 일치하지 않는 바이트가 포함되어 있지 않은 경우에만 선언 될 수 있는 변수 정의에 저장됩니다 _.

LC_OLD=$LC_ALL LC_ALL=C
alias "${nm##*[!_A-Z0-9a-z]*}=_$nm=\$val" &&
eval "${nm##[0-9]*}" && unalias "$nm"
LC_ALL=$LC_OLD

eval여기 alias에서 varname 에서 새 호출을 처리하는 데 사용됩니다 . 그러나 이전 alias정의가 성공한 경우에만 호출됩니다 . 많은 다른 구현에서 alias이름에 대해 다른 종류의 값을 받아 들일 것이라는 것을 알고 있지만 완전히 빈 값을 허용하는 값으로 아직 실행되지 않았습니다. .

그러나의 정의 alias는에 대한 _$nm것이며 이는 중요한 환경 값을 덮어 쓰지 않도록하기위한 것입니다. 나는 a로 시작하는 주목할만한 환경 값을 알지 못하며 _일반적으로 semi-private 선언에 대한 안전한 내기입니다.

어쨌든, alias정의가 성공하면 의 값에 대해 alias명명 된 이름을 선언합니다 $nm. 그리고 숫자로 시작하지 않으면 eval전화 할 alias것입니다.eval 에만 널 인수를 가져옵니다. 따라서 두 조건이 모두 충족 eval되면 별칭이 호출되고 별칭에 저장된 변수 정의 alias가 생성 된 후 해시 테이블에서 새 변수 가 즉시 제거됩니다.


;변수 이름에는 사용할 수 없습니다. 당신의 내용을 제어 할 수없는 경우 $name에, 당신은 그것을 소독해야 export/ declare도합니다. 하지만이 export같은 몇 가지 변수를 설정, 코드를 실행하지 않는 PATH, PS4그 중 대부분은 info -f bash -n 'Bash Variables'똑같이 위험한 부작용이있다.
Stéphane Chazelas

@ StéphaneChazelas-물론 허용되지 않지만 이전과 마찬가지로 eval첫 번째 패스 에서 변수 이름이 아닙니다 -변수 확장입니다. 다른 곳에서 말했듯이, 그 맥락에서 그것은 매우 많이 허용됩니다. 여전히 $ PATH 인수는 매우 좋은 것입니다-작은 편집을하고 나중에 추가 할 것입니다.
mikeserv

@ StéphaneChazelas-결코 늦지 않는 것이 낫다 ...?
mikeserv

실제로, zsh, pdksh, mksh, yash에 불평하지 않습니다 unset 'a;b'.
Stéphane Chazelas

당신도 원할 unset -v -- ...것입니다.
Stéphane Chazelas
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.