programing

Bash의 'eval' 명령어와 그 일반적인 용도

megabox 2023. 4. 14. 21:35
반응형

Bash의 'eval' 명령어와 그 일반적인 용도

Bash man 페이지를 읽고 투고에 관해, 나는 아직도 정확히 무엇을 이해하는데 어려움을 겪고 있다.eval명령어를 사용하면 일반적인 용도가 됩니다.

예를 들어 다음과 같습니다.

$ set -- one two three  # Sets $1 $2 $3
$ echo $1
one

$ n=1
$ echo ${$n}       ## First attempt to echo $1 using brackets fails
bash: ${$n}: bad substitution

$ echo $($n)       ## Second attempt to echo $1 using parentheses fails
bash: 1: command not found

$ eval echo \${$n} ## Third attempt to echo $1 using 'eval' succeeds
one

여기서 정확히 무슨 일이 일어나고 있고 달러 기호와 백슬래시는 어떻게 이 문제와 관련되는가?

eval (여러 인수를 전달하면 먼저 인수 사이에 공백이 포함되어 있습니다).

${$n}쾅쾅 때리다중괄호 안에는 변수 이름과 일부 접두사 및 접미사만 사용할 수 있지만 임의 bash 구문은 사용할 수 없으며 특히 변수 확장을 사용할 수 없습니다.값, '이 변수에 이름이 있는 변수의 값'이라는 표현도 .

echo ${!n}
one

$(…)는 (즉, 현재 셸에서 변수 값 등의 모든 설정을 상속하는 개별 프로세스에서) 괄호 안에 지정된 명령을 실행하여 출력을 수집합니다. ★★★★★★★★★★★★★★★★★.echo $($n)$n포탄★★$n1,$($n) " " " 를 하려고 합니다.1존재하지 않습니다.

eval echo \${$n}합니다.eval 확장 가 장음음음음음음음음음음음음음음 . . . .이다.echo ★★★★★★★★★★★★★★★★★」${1}eval echo \${$n}합니다.echo ${1}.

및 '변수 치환'이 $):"$foo", "$(foo)"변수와 명령어 대체를 꺼야 하는 경우를 제외하고 항상 큰따옴표를 붙입니다.이중 따옴표가 없으면 셸은 필드 분할(즉, 변수의 값 또는 명령어의 출력을 다른 단어로 분할)을 수행한 다음 각 단어를 와일드카드 패턴으로 처리합니다.예를 들어 다음과 같습니다.

$ ls
file1 file2 otherfile
$ set -- 'f* *'
$ echo "$1"
f* *
$ echo $1
file1 file2 file1 file2 otherfile
$ n=1
$ eval echo \${$n}
file1 file2 file1 file2 otherfile
$eval echo \"\${$n}\"
f* *
$ echo "${!n}"
f* *

eval을 사용하다일부 셸에서는 런타임까지 이름이 알려지지 않은 변수의 값을 얻는 것이 가장 일반적인 용도입니다.가 있기 에 이것은 필요하지 않습니다.${!VAR}구문을 사용합니다. eval연산자, 예약된 단어 등을 포함하는 더 긴 명령을 구성해야 할 경우에도 유용합니다.

eval은 단순히 "실행 전에 표현을 한 번 더 평가하는 것"이라고 생각하시면 됩니다.

eval echo \${$n} becomes가 되다echo $11월 1일"CHANGE: "CHANGE:

  • \$ became became가 되었다$ 않으면 ).백슬래시가 필요 없는 경우 백슬래시가${$n}인 '{$n}되지 않습니다.) (그것은 허용되지 않습니다.)
  • $n 평가받았습니다.1
  • eval를 감추다

echo $1직접 실행할 수 있습니다.

★★★★★★★★★★★★★★★★★.eval <some command> 평가하겠습니다.<some command>(여기서 evaluate는 대체 변수, 이스케이프 문자를 올바른 문자로 바꾸는 등의 의미)그리고 나서, 결과 식을 다시 실행합니다.

eval변수를 동적으로 작성하거나 이와 같이 읽도록 특별히 설계된 프로그램에서 출력을 읽을 때 사용합니다.예에 대해서는 명령 및 보안 문제 평가를 참조하십시오.이 링크에는 일반적인 방법들이 포함되어 있습니다.eval사용되는 것과 그에 따른 리스크가 있습니다.

경험상 eval의 "일반적인" 용도는 셸 명령어를 생성하여 환경변수를 설정하는 명령어를 실행하는 것입니다.

환경변수 컬렉션을 사용하는 시스템과 설정할 필요가 있는 변수와 값을 결정하는 스크립트 또는 프로그램이 있을 수 있습니다.스크립트 또는 프로그램을 실행할 때마다 분기된 프로세스로 실행되므로 종료 시 환경 변수에 직접 수행하는 모든 작업이 손실됩니다.그러나 이 스크립트 또는 프로그램은 내보내기 명령을 표준 출력으로 전송할 수 있습니다.

eval을 사용하지 않을 경우 표준 출력을 임시 파일로 리디렉션하고 임시 파일을 찾은 다음 삭제해야 합니다.eval을 사용하면 다음 작업을 수행할 수 있습니다.

eval "$(script-or-program)"

인용문은 중요합니다.다음 (제어된) 예를 들어보겠습니다.

# activate.sh
echo 'I got activated!'

# test.py
print("export foo=bar/baz/womp")
print(". activate.sh")

$ eval $(python test.py)
bash: export: `.': not a valid identifier
bash: export: `activate.sh': not a valid identifier

$ eval "$(python test.py)"
I got activated!

eval 문은 셸에 대해 eval 인수를 명령으로 사용하여 명령줄에서 실행하도록 지시합니다.다음과 같은 상황에서 유용합니다.

스크립트에서 명령을 변수로 정의하고 나중에 해당 명령을 사용하려면 eval을 사용해야 합니다.

a="ls | more"
$a

출력:

bash: 명령어를 찾을 수 없음: ls | more

위의 명령어는 ls가 이름 파이프(|) 등을 사용하여 파일을 나열하려고 했기 때문에 작동하지 않았습니다.그러나 다음 파일은 없습니다.

eval $a

출력:

파일.txt
" 아이디
.remote_buff.sh
§ .txt txt

업데이트: 어떤 사람들은 절대 평가를 사용해서는 안 된다고 말합니다.동의하지 않습니다.될 수 경우 위험이 발생한다고 생각합니다.eval그러나 리스크가 되지 않는 일반적인 상황이 많이 있기 때문에 어떤 경우에도 eval을 사용하는 방법을 알 필요가 있습니다.스택오버플로우 답변은 평가의 위험과 평가의 대안을 설명합니다.최종적으로 평가가 안전하고 효율적인지 여부는 사용자에게 달려 있습니다.


★★eval scriptstatement를 하여 계산되거나 취득된 코드 을 bash script.statement를 하여 실행할 수 .

스크립트를 각 bash를 사용하는 .eval순서대로 실행할 수 있습니다.으로 bash와 입니다.source이것은 Import된 스크립트의 내용에 대해 어떤 변환(필터링 또는 치환 등)을 수행할 필요가 없는 한 사용하는 스테이트먼트입니다.

eval그러나 다른 변수에 할당된 문자열에 이름이 포함된 변수를 읽거나 쓰는 것이 유용하다는 것을 알게 되었습니다.예를 들어, 코드 설치 공간을 작게 유지하고 중복을 방지하면서 변수 집합에 대한 작업을 수행합니다.

eval개념적으로 간단합니다. 해석 를 미세 하여 bash를 bash로 만들 수 .eval이해하기 어렵고 사용하기 어렵다.을 사용하다

  1. 은 론은 to to to to로 .eval는 실행 시 계산되는 문자열식입니다. eval는 인수의 최종 해석 결과를 스크립트의 실제 코드 행으로 실행합니다.

  2. 을 사용하다가 실행 코드 스크립트의 내에서 은 "bash"에서 크래시됩니다.eval이치노

  3. 할 때, '어울리지 않다'를할 수 .eval「」가 붙은 .echo표시되는 것을 봐 주세요.컨텍스트에서 를 통해 eval과가있있 있있있다다


다음 예는 평가의 구조를 명확히 하는 데 도움이 될 수 있습니다.

예 1:

evalnormal' 문은 'normal'입니다.

$ eval a=b
$ eval echo $a
b

의 예에서는 첫 "" " " " " " " "eval스테이트먼트는 목적이 없으므로 삭제할 수 있습니다. eval코드에는 동적 측면이 없기 때문에 첫 번째 줄에서는 의미가 없습니다.즉, bash 코드의 마지막 줄에 이미 구문 해석되어 있기 때문에 bash 스크립트의 일반 코드 문과 동일합니다. ★★★★★★★★★★★★★★★.eval 단계가 이 필요 없기 때문에왜냐하면 파싱 스텝이 있기 때문에$a리터럴 스트링에 상당하는 것은, 인다이렉션(예를 들면, 실제의 bash 명사 또는 bash-held 스크립트 변수의 스트링 값을 개입시켜 참조하지 않는다)이 없기 때문에, 이 문자열은, 코드의 행과 같이 동작합니다.eval프레픽스



예 2:

문자열 값으로 전달된 변수 이름을 사용하여 변수 할당을 수행합니다.

$ key="mykey"
$ val="myval"
$ eval $key=$val
$ echo $mykey
myval

만약 당신이 한다면echo $key=$val을 사용하다

mykey=myval

는 문자열 해석의 최종 결과이므로 평가에 의해 실행되며, 따라서 마지막에 에코 스테이트먼트의 결과가 됩니다.



예 3:

예 2에 대한 간접 추가

$ keyA="keyB"
$ valA="valB"
$ keyB="that"
$ valB="amazing"
$ eval eval \$$keyA=\$$valA
$ echo $that
amazing

위의 예는 앞의 예보다 조금 더 복잡하며 bash의 해석 순서와 특성에 크게 의존하고 있습니다.eval행은 대략적으로 다음 순서로 내부에서 해석됩니다(다음 문장은 실제 코드가 아닌 의사 코드이며 최종 결과에 도달하기 위해 내부적으로 어떻게 분할되는지를 나타내기 위한 것입니다).

 eval eval \$$keyA=\$$valA  # substitution of $keyA and $valA by interpreter
 eval eval \$keyB=\$valB    # convert '$' + name-strings to real vars by eval
 eval $keyB=$valB           # substitution of $keyB and $valB by interpreter
 eval that=amazing          # execute string literal 'that=amazing' by eval

가정된 구문 분석 순서가 어떤 평가가 충분한지 설명하지 못할 경우, 세 번째 예에서는 진행 상황을 명확히 하기 위해 구문 분석을 더 자세히 설명할 수 있습니다.



예 4:

이름이 문자열에 포함된 변수 자체에 문자열 값이 포함되어 있는지 여부를 확인합니다.

a="User-provided"
b="Another user-provided optional value"
c=""

myvarname_a="a"
myvarname_b="b"
myvarname_c="c"

for varname in "myvarname_a" "myvarname_b" "myvarname_c"; do
    eval varval=\$$varname
    if [ -z "$varval" ]; then
        read -p "$varname? " $varname
    fi
done

첫 번째 반복:

varname="myvarname_a"

를 Bash로 eval , , , , 입니다.eval 런타임에 볼 수 . :, 、 sees 、 말 、 음 음 음 、 sees sees sees sees sees sees sees sees sees sees sees 。

eval varval=\$$myvarname_a

다음 의사 코드는 bash가 위의 실제 코드 행을 어떻게 해석하여 에 의해 실행되는 최종 값에 도달하는지를 설명하려고 합니다.eval (은 정확한 코드가 을 나타냅니다 (다음 행은 정확한 bash 코드가 아닌 설명을 나타냅니다.)

1. eval varval="\$" + "$varname"      # This substitution resolved in eval statement
2. .................. "$myvarname_a"  # $myvarname_a previously resolved by for-loop
3. .................. "a"             # ... to this value
4. eval "varval=$a"                   # This requires one more parsing step
5. eval varval="User-provided"        # Final result of parsing (eval executes this)

그 그 합니다.파싱은 특별히 불분명한 .eval복잡성은 인수의 해석에 있습니다.

varval="User-provided"

위의 예에서 나머지 코드는 $varval에 할당된 값이 늘인지 여부를 테스트하고 값을 입력하도록 사용자에게 요구합니다.

나는 원래 평가의 사용법을 배운 적이 없다. 왜냐하면 대부분의 사람들은 역병처럼 평가에서 멀리 떨어지라고 권할 것이기 때문이다.그러나 최근 그것을 빨리 인식하지 못한 것에 대해 얼굴을 붉히는 사용 사례를 발견했습니다.

테스트하기 위해 대화식으로 실행하는 cron 작업이 있는 경우 cat을 사용하여 파일 내용을 보고 cron 작업을 복사하여 붙여넣을 수 있습니다.불행히도, 이것은 마우스를 만지는 것과 관련이 있는데, 이것은 제 생각에 죄악입니다.

/etc/cron.d/repeatme에 cron 작업이 있고 다음 내용이 있다고 가정합니다.

*/10 * * * * root program arg1 arg2

모든 정크가 앞에 있는 스크립트로 실행할 수는 없지만 컷을 사용하여 모든 정크를 제거하고 하위 쉘로 랩하여 eval을 사용하여 문자열을 실행할 수 있습니다.

eval $( cut -d ' ' -f 6- /etc/cron.d/repeatme)

cut 명령어는 공백으로 구분된 파일의 6번째 필드만 출력합니다.그런 다음 Eval이 해당 명령을 실행합니다.

여기서는 cron 작업을 예로 사용했는데, 이 작업의 개념은 stdout에서 텍스트를 포맷하고 그 텍스트를 평가하는 것입니다.

이 경우 평가의 사용은 안전하지 않습니다. 왜냐하면 우리는 사전에 무엇을 평가할 것인지 정확히 알고 있기 때문입니다.

나는 최근에 그것을 사용해야만 했다.eval필요한 순서대로 여러 개의 가새 확장을 평가하도록 강제합니다.여러 의 브레이스 때문에 Bash는 왼쪽에서 오른쪽으로 확장합니다.

xargs -I_ cat _/{11..15}/{8..5}.jpg

까지 확장하다.

xargs -I_ cat _/11/8.jpg _/11/7.jpg _/11/6.jpg _/11/5.jpg _/12/8.jpg _/12/7.jpg _/12/6.jpg _/12/5.jpg _/13/8.jpg _/13/7.jpg _/13/6.jpg _/13/5.jpg _/14/8.jpg _/14/7.jpg _/14/6.jpg _/14/5.jpg _/15/8.jpg _/15/7.jpg _/15/6.jpg _/15/5.jpg

하지만 두 번째 버팀대 확장을 먼저 하고

xargs -I_ cat _/11/8.jpg _/12/8.jpg _/13/8.jpg _/14/8.jpg _/15/8.jpg _/11/7.jpg _/12/7.jpg _/13/7.jpg _/14/7.jpg _/15/7.jpg _/11/6.jpg _/12/6.jpg _/13/6.jpg _/14/6.jpg _/15/6.jpg _/11/5.jpg _/12/5.jpg _/13/5.jpg _/14/5.jpg _/15/5.jpg

내가 생각해낼 수 있는 가장 좋은 방법은

xargs -I_ cat $(eval echo _/'{11..15}'/{8..5}.jpg)

은 작은 첫 됩니다.eval라인은, 「커맨드 라인」에 의해서 됩니다.eval.

네스트 버팀대 확장을 포함한 교활한 계략이 있을 수 있지만, 한 번에 이런 일이 일어날 수 있다면, 나는 너무 늙고 멍청해서 그것을 볼 수 없다.

일반적인 용도를 물으셨습니다.

셸 스크립팅에 관한 일반적인 불만 중 하나는 (아마도) 값을 함수에서 되돌리기 위해 참조를 통과할 수 없다는 것입니다.

하지만 실제로는 "eval"을 통해 참조로 통과할 수 있습니다.착신측은, 발신자에 의해서 평가되는 변수 할당의 리스트를 반송할 수 있습니다.발신자가 결과 변수의 이름을 지정할 수 있으므로 참조에 의해 전달됩니다(아래 예 참조).오류 결과는 errno 및 errstr 등의 표준 이름을 반환할 수 있습니다.

다음은 bash에서 참조에 의해 전달되는 예를 보여드리겠습니다.

#!/bin/bash
isint()
{
    re='^[-]?[0-9]+$'
    [[ $1 =~ $re ]]
}

#args 1: name of result variable, 2: first addend, 3: second addend 
iadd()
{
    if isint ${2} && isint ${3} ; then
        echo "$1=$((${2}+${3}));errno=0"
        return 0
    else
        echo "errstr=\"Error: non-integer argument to iadd $*\" ; errno=329"
        return 1
    fi
}

var=1
echo "[1] var=$var"

eval $(iadd var A B)
if [[ $errno -ne 0 ]]; then
    echo "errstr=$errstr"
    echo "errno=$errno"
fi
echo "[2] var=$var (unchanged after error)"

eval $(iadd var $var 1)
if [[ $errno -ne 0 ]]; then
    echo "errstr=$errstr"
    echo "errno=$errno"
fi  
echo "[3] var=$var (successfully changed)"

출력은 다음과 같습니다.

[1] var=1
errstr=Error: non-integer argument to iadd var A B
errno=329
[2] var=1 (unchanged after error)
[3] var=2 (successfully changed)

그 텍스트 출력에는 거의 무제한의 밴드 폭이 있습니다!그리고 여러 출력 라인을 사용할 경우 더 많은 가능성이 있습니다. 예를 들어, 첫 번째 줄은 변수 할당에 사용할 수 있고 두 번째 줄은 지속적인 '생각의 흐름'에 사용할 수 있지만, 이 게시물의 범위를 벗어납니다.

질문:

who | grep $(tty | sed s:/dev/::)

파일 a와 tty가 존재하지 않는다고 주장하는 오류를 출력합니다.이것은 grep 실행 전에 tty가 해석되는 것이 아니라 bash가 tty를 grep에 매개 변수로서 전달한 것을 의미하는 것으로 이해했습니다.

또한 중첩된 리다이렉션은 자 프로세스를 지정해야 하는 일치하는 괄호로 처리되어야 하지만 bash는 기본적으로 워드 구분자이며 프로그램에 전송되는 파라미터를 생성하기 때문에 괄호는 먼저 일치하지 않고 표시된 것으로 해석됩니다.

grep에 대해 구체적으로 설명하고 파이프 대신 파일을 파라미터로 지정했습니다.또한 명령어의 출력을 파일로 전달하여 기본 명령어를 단순화하여 I/O 파이프가 중첩되지 않도록 했습니다.

grep $(tty | sed s:/dev/::) <(who)

잘 동작합니다.

who | grep $(echo pts/3)

는 그다지 바람직하지 않지만 중첩된 파이프를 제거할 뿐만 아니라 잘 작동합니다.

결론적으로 bash는 중첩된 피핑을 좋아하지 않는 것 같습니다.bash는 재귀적으로 작성된 새로운 웨이브 프로그램이 아니라는 것을 이해하는 것이 중요합니다.대신 bash는 기능이 추가된 오래된 1,2,3 프로그램입니다.하위 호환성을 보장하기 위해 해석의 초기 방법은 변경되지 않았습니다.bash가 첫 번째 일치 괄호로 고쳐 쓴 경우, 몇 개의 bash 프로그램에 몇 개의 버그가 도입됩니까?많은 프로그래머들이 수수께끼가 되는 것을 좋아한다.

와 같이 예는 bash 각 clearlight를 이 될 입니다.eval순서대로 실행할 수 있습니다.저는 전문가는 아니지만, 현재 제가 읽고 있는 교과서(위르겐 볼프의 셸-프로그래머룽)는 이 사용법을 특히 한 가지 언급하고 있습니다.여기서 수집된 잠재적인 사용 사례에 중요한 추가사항이 될 것 같습니다.

디버깅을 위해 스크립트를 한 줄씩 살펴볼 수 있습니다(스텝마다 Enter 키를 누릅니다). 하면 .evalDEBUG 신호를 트랩하여 모든 회선을 실행합니다(모든 회선 후에 전송된다고 생각합니다).

trap 'printf "$LINENO :-> " ; read line ; eval $line' DEBUG

"실행 전에 당신의 표현을 한 번 더 평가한다"는 답변이 마음에 들어 다른 예를 들어 명확히 하고 싶습니다.

var="\"par1 par2\""
echo $var # prints nicely "par1 par2"

function cntpars() {
  echo "  > Count: $#"
  echo "  > Pars : $*"
  echo "  > par1 : $1"
  echo "  > par2 : $2"

  if [[ $# = 1 && $1 = "par1 par2" ]]; then
    echo "  > PASS"
  else
    echo "  > FAIL"
    return 1
  fi
}

# Option 1: Will Pass
echo "eval \"cntpars \$var\""
eval "cntpars $var"

# Option 2: Will Fail, with curious results
echo "cntpars \$var"
cntpars $var

옵션 2의 흥미로운 결과는 다음과 같은 두 가지 파라미터를 통과시켰다는 것입니다.

  • 번째 파라미터: " " " " 입니다."par1
  • 번째 파라미터: " " " " 입니다.par2"

어게 카카 운감 감? ???의 「」eval고칠 수 있을 거야

이것은 "Bash를 사용하여 변수에 대한 파일을 참조하는 방법"에 대한 다른 답변에서 수정되었습니다.

언급URL : https://stackoverflow.com/questions/11065077/the-eval-command-in-bash-and-its-typical-uses

반응형