Shell에서 eval의 유용한 사용 사례는 무엇입니까?

Shell에서 eval의 유용한 사용 사례는 무엇입니까?

eval is evilShell/POSIX 세계와 Python 등과 같은 다른 언어에서 많이 듣게 됩니다 .

그런데 정말 쓸모가 없는 걸까? 아니면 신비하고 문서화되지 않았으며 흥미롭거나 유용한 사용 사례가 있습니까?

대답이 sh/ bash중심적이면 더 좋겠지만, 다른 쉘과도 관련이 있으면 좋을 것입니다.

PS: 저는요훌륭한 알고 있는 평가를 고려하십시오 evil.

답변1

나는 두 가지...일반적인...사용 사례를 알고 있습니다 eval.

  1. 매개변수 처리getopt:

    [T]이 구현은 인용된 출력을 생성할 수 있으며, 이는 쉘에서 다시 해석되어야 합니다(일반적으로 eval 명령을 사용하여).

  2. 설정하다SSH 에이전트:

    [T]에이전트는 호출 셸에서 평가할 수 있는 필수 셸 명령(sh(1) 또는 csh(1) 구문을 생성할 수 있음)을 인쇄합니다(예: eval `ssh-agent -s`sh(1) 또는 ksh( 1 )와 같은 Bourne 유형 셸의 경우) eval `ssh-agent -c`csh(1) 및 파생물 .

두 가지 용도 모두에 다른 옵션이 있을 수 있지만 둘 중 어느 것에도 놀라지 않을 것입니다.

답변2

Bash & Co. slicing (예:) 과 같은 확장 없이 POSIX 셸에서 마지막 인수를 얻으려면 다음을 ${@: -1}사용할 수 있습니다.

eval "v=\${$#}" 

$#이는 쉘 내부이고 스크립트/함수의 인수 수만 포함할 수 있으므로 불쾌한 트릭에 취약하지 않습니다.

이것은 내가 생각해낸 것이 아니다.스티븐 차제라스 코멘트. 에서도 언급됨이 답변eval을 사용하지 말아야 하는 이유와 시기는 무엇입니까?.

답변3

더 나은 대안을 찾을 수 없었지만 eval작업을 깔끔하게 완료한 "실제" 사용 사례의 몇 가지 예입니다.


"조건부 확장" 사용 사례. 여기서는 $rmsg_pfx값이 있는 경우 에만 리디렉션을 사용하고 싶습니다 .

eval 'printf -- %s%s\\n "$rmsg_pfx" "$line" '"${rmsg_pfx:+>&2}"

없이는 이 작업을 수행할 수 없습니다. eval왜냐하면 비트가 리디렉션이 아닌 >&2매개변수로 확장되기 때문입니다.printf

$rmsg_pfxnull인지 확인하기 위해 줄을 복사할 수 있지만 그건... 음... 코드 중복이 될 것입니다.


리디렉션에 대해 말하자면, "간접" 사용 사례로서 저는 {varname}>&...리디렉션 구문에 의존하는 것을 좋아합니다. 저는 다음과 같이 POSIXly를 조롱합니다.

# equivalent of bash/ksh `exec {rses_fd0}>&- {rses_fd1}<&-` redirection syntax
eval "exec $rses_fd0>&- $rses_fd1<&-"

위의 내용은 fds를 닫는 것입니다. 마찬가지로 fds 열기를 시뮬레이션하기 위해 비슷한 간접 작업을 수행하고 있습니다. 분명히 $rses_fd0$rses_fd1는 스크립트의 내부 변수이며 처음부터 끝까지 스크립트에 의해 완전히 제어됩니다.


eval때로는 다른 쉘을 방해하지 않고 특정 쉘을 위한 쉘 코드 조각을 단순히 "보호" 해야 할 때도 있습니다 .

예를 들어, 다음 코드는 일부 셸별 최적화도 포함하는 이식 가능한(POSIXly) 스크립트에서 가져온 것입니다.

sochars='][ (){}:,!'"'\\"
# NOTE: wrapped in an eval to protect it from dash which croaks over the regex
eval 'o=; while [[ "$s" =~ ([^$sochars]*)([$sochars])(.*) ]]; do
    ...
done'

dash해당 구문이 실제 코드 경로에 전혀 들어가지 않더라도 어휘 수준에서 알 수 없는(그러나 직접적인) 구문에 의해 차단되는 것입니다.


다른 의미에서 "보호"의 또 다른 사용 사례입니다. 때때로 나는 보존과 복원 목적을 위해 "있을 것 같지 않은" 이름을 만들기에는 너무 게으릅니다. 예를 들어 다음 경우에는 $r값만 유지 하고 싶습니다 .

# wrapped in eval just to make sure that $r is not overwritten by (the call chain of) coolf
eval '
    coolf "$tmp" || return "$lerrno"'"
    return $r
"

실제로 저는 다음과 같이 정리 작업을 수행하는 동시에 루프 모음의 종료 상태를 유지하기 위해 위의 트릭을 자주 사용합니다.

    done <&3
    eval "unset ret vals; exec 3<&-; return $?"
}

또는 "지연된 실행"과 같은 위와 유사한 상황에서:

    done
    # return boolean set by loop while also unsetting it
    eval "unset ok; ${ok:-false}"
}

위의 두 코드 조각의 암시적 의도는 특히 함수가 대화형으로 실행되도록 의도된 경우 함수 실행에 "아티팩트"를 남기지 않는 것입니다. 후자의 경우 다음과 같이 할 수 있습니다.

[ "${ok:-false}" = false ] && { unset ok; return 1; } || { unset ok; return 0; }

하지만 제가 보기에는 거칠어 보입니다.


마지막으로, 작은 동작 변경이나 호출자에 대한 일부 후크 지원을 위해 호출 기반으로 함수를 약간 수정하거나 확장해야 하는 경우가 가끔 있습니다. 콜백과 비슷하지만 "인라인" 방식에서는 덜 번거로워 보입니다. 특히 후크 조각이 함수의 자체 $@매개변수에 액세스해야 하는 경우에는 더욱 그렇습니다. 물론 변수를 통해 제공되고 나중에 eval함수에 의해 편집되는 이러한 조각 자체는 완전히 정적/수작업으로 제작되거나 사전 제어/위생 처리됩니다.

답변4

eval내가 본 실제 용도 중 하나는 다음과 같습니다.Fluxbox.startfluxbox.dbus.diff.gz 슬랙웨어에서. 다음과 같습니다.

# Start DBUS session bus:
if [ -z "$DBUS_SESSION_BUS_ADDRESS" ]; then
   eval $(dbus-launch --sh-syntax --exit-with-session)
fi

이 사용법은 eval수백만 명의 사람들에 의해 테스트되지 않았지만(Slackware는 그다지 일반적이지 않음) 작업을 완료합니다. 그래도 나는 eval쉘 스크립트에서 이것을 피하려고 노력합니다. 예를 들어 배열을 구현하거나 변수 간접 지정을 수행하는 등 필요하다고 느끼면 Bash로 전환하고, 여전히 필요하다고 느끼면 스크립트 디자인을 다시 생각하거나 완전히 다른 언어로 전환합니다.

관련 정보