eval과 exec의 차이점은 무엇입니까? 실행하는 bash

eval그리고 exec명령을 실행하는 bash (1) 명령이 내장되어 있습니다.

또한 exec몇 가지 옵션이 있지만 유일한 차이점은 무엇입니까? 그들의 상황은 어떻게됩니까?



답변

evalexec완전히 다른 짐승이다. (둘 다 명령을 실행하지만 쉘에서 수행하는 모든 작업을 수행한다는 사실을 제외하고)

$ help exec
exec: exec [-cl] [-a name] [command [arguments ...]] [redirection ...]
    Replace the shell with the given command.

무엇 exec cmd않습니다, 단지 실행과 정확히 동일 cmd대신 별도의 프로세스로 실행되고의 현재 쉘이 명령으로 대체되는 것을 제외하고. 내부적으로 say /bin/ls를 실행 fork()하면 하위 프로세스를 생성하기 위해 호출 한 다음 exec()하위에서 실행 /bin/ls합니다. exec /bin/ls한편 것입니다 하지 포크, 그러나 다만 쉘을 대체합니다.

비교:

$ bash -c 'echo $$ ; ls -l /proc/self ; echo foo'
7218
lrwxrwxrwx 1 root root 0 Jun 30 16:49 /proc/self -> 7219
foo

$ bash -c 'echo $$ ; exec ls -l /proc/self ; echo foo'
7217
lrwxrwxrwx 1 root root 0 Jun 30 16:49 /proc/self -> 7217

echo $$내가 시작한 쉘의 PID를 인쇄하고, 리스팅 /proc/selfls쉘에서 실행 된 PID를 제공합니다 . 일반적으로 프로세스 ID는 다르지만 exec쉘이 있고 ls프로세스 ID가 동일합니다. 또한 exec쉘이 교체되었으므로 다음 명령 이 실행되지 않았습니다.


반면에 :

$ help eval
eval: eval [arg ...]
    Execute arguments as a shell command.

eval현재 쉘에서 명령으로 인수를 실행합니다. 다시 말해서 eval foo bar, 그냥와 동일합니다 foo bar. 그러나 변수는 실행하기 전에 확장되므로 쉘 변수에 저장된 명령을 실행할 수 있습니다.

$ unset bar
$ cmd="bar=foo"
$ eval "$cmd"
$ echo "$bar"
foo

그것은 것입니다 하지 변수가 현재 쉘에 설정되어 있으므로, 자식 프로세스를 만들 수 있습니다. (물론 eval /bin/ls평범한 노인과 같은 방식으로 자식 프로세스를 만듭니다 /bin/ls.)

또는 쉘 명령을 출력하는 명령이있을 수 있습니다. Running ssh-agent은 백그라운드에서 에이전트를 시작하고 현재 셸에서 설정하고 자식 프로세스 ( ssh실행할 명령)에서 사용할 수있는 많은 변수 할당을 출력합니다 . 따라서 다음 ssh-agent과 같이 시작할 수 있습니다.

eval $(ssh-agent)

그리고 현재 쉘은 다른 명령이 상속 할 변수를 얻습니다.


물론 변수에 cmd와 같은 내용이 포함되어 있으면 rm -rf $HOMErunning eval "$cmd"은 원하지 않는 일입니다. 그래서 문자열 내부 명령 대체처럼 심지어 일들이 처리 될 사람이해야 정말 입력이 할 수 있는지 확인 eval을 사용하기 전에 안전합니다.

종종 eval실수로 코드와 데이터를 잘못 혼합하는 것을 피하고 피할 수 있습니다.


답변

exec새로운 프로세스를 만들지 않습니다. 그것은 대체하는 새로운 명령을 사용하여 현재 프로세스를. 명령 행에서이 작업을 수행하면 셸 세션이 효과적으로 종료됩니다 (그리고 로그 아웃하거나 터미널 창을 닫을 수도 있습니다).

예 :

ksh% bash
bash-4.2$ exec /bin/echo hello
hello
ksh% 

여기에 있습니다 ksh(일반 쉘). 나는 bashbash를 시작 하고 시작한다 exec /bin/echo. 프로세스가로 교체 되었으므로 ksh나중에 다시 탈퇴 한 것을 확인할 수 있습니다 .bash/bin/echo


답변

TL; DR

exec명령이 지정되지 않은 경우 현재 쉘 프로세스를 새 것으로 대체하고 스트림 경로 재 지정 / 파일 디스크립터를 처리하는 데 사용됩니다. eval문자열을 명령으로 평가하는 데 사용됩니다. 둘 다 런타임에 알려진 인수로 명령을 빌드하고 실행하는 데 사용될 수 있지만 exec명령을 실행하는 것 외에도 현재 쉘의 프로세스를 대체합니다.

exec buil-in

통사론:

exec [-cl] [-a name] [command [arguments]]

이 내장 명령이 지정된 경우 매뉴얼에 따라

… 쉘을 대체합니다. 새로운 프로세스가 생성되지 않습니다. 인수는 명령에 대한 인수가됩니다.

다시 말해, bashPID 1234 로 실행 중이고 exec top -u root해당 쉘 내 에서 실행 중이 면 top명령에 PID 1234가 있고 쉘 프로세스를 대체합니다.

이것은 어디에 유용합니까? 래퍼 스크립트라고합니다. 이러한 스크립트는 인수 세트를 작성하거나 환경에 전달할 변수에 대한 특정 결정을 내린 후 exec지정된 명령으로 대체하고 래퍼 스크립트가 작성하는 것과 동일한 인수를 제공하는 데 사용합니다.

매뉴얼에 명시된 내용은 다음과 같습니다.

명령을 지정하지 않으면 모든 재 지정이 현재 쉘에서 적용됩니다.

이를 통해 현재 쉘 출력 스트림에서 파일로 무엇이든 리디렉션 할 수 있습니다. 이것은 stdout명령 을 보지 않고 싶을 때 로깅 또는 필터링 목적으로 유용 할 수 있습니다 stderr. 예를 들면 다음과 같습니다.

bash-4.3$ exec 3>&1
bash-4.3$ exec > test_redirect.txt
bash-4.3$ date
bash-4.3$ echo "HELLO WORLD"
bash-4.3$ exec >&3
bash-4.3$ cat test_redirect.txt
2017 05 20 星期六 05:01:51 MDT
HELLO WORLD

이 동작은 쉘 스크립트로그인하고 , 스트림을 별도의 파일이나 프로세스로 리디렉션 하고, 파일 디스크립터가있는 기타 재미있는 것들 에 편리합니다.

bash버전 4.3 이상의 소스 코드 레벨에서 exec기본 제공은에 정의되어 builtins/exec.def있습니다. 수신 된 명령을 구문 분석하고 파일이 있으면 파일에 shell_execve()정의 된 기능 에 전달 execute_cmd.c합니다.

간단히 말해서 execC 프로그래밍 언어로 된 명령 계열이 shell_execve()있으며 기본적으로 다음과 같은 래퍼 함수입니다 execve.

/* Call execve (), handling interpreting shell scripts, and handling
   exec failures. */
int
shell_execve (command, args, env)
     char *command;
     char **args, **env;
{

eval 내장

bash 4.3 매뉴얼 상태 (내가 강조한 내용) :

인수는 읽고 단일 명령으로 함께 연결됩니다. 그런 다음이 명령을 에서 읽고 실행 하며 종료 상태는 eval 값으로 리턴됩니다.

프로세스 교체가 발생하지 않습니다. 기능 exec을 시뮬레이션하는 것이 목표 인 것과 달리 내장 execve()기능 eval은 마치 사용자가 명령 행에 인수를 입력 한 것처럼 인수를 “평가”하는 역할 만합니다. 따라서 새로운 프로세스가 생성됩니다.

이것이 유용한 곳은 어디입니까? Gilles 가이 답변에서 지적했듯이 “… eval은 자주 사용되지 않습니다. 일부 쉘에서 가장 일반적인 용도는 런타임까지 이름을 알 수없는 변수의 값을 얻는 것입니다.” 개인적으로, 나는 우분투에서 사용자가 현재 사용하고있는 특정 작업 공간을 기반으로 명령을 실행 / 평가 해야하는 두 스크립트에서 사용했습니다.

소스 코드 레벨에서 정의되고 builtins/eval.def구문 분석 된 입력 문자열을 evalstring()함수에 전달합니다 .

무엇보다도, eval변수에 할당 있는 동안은, 현재 쉘 실행 환경에 남아 exec할 수 없습니다 :

$ eval x=42
$ echo $x
42
$ exec x=42
bash: exec: x=42: not found

답변

새로운 자식 프로세스를 생성하고 인수를 실행하고 종료 상태를 반환합니다.

어? 요점은 eval자식 프로세스를 만들지 않는다는 것입니다. 만약 내가한다면

eval "cd /tmp"

쉘에서, 현재 쉘은 디렉토리를 변경했을 것입니다. 또한 exec새로운 자식 프로세스를 만들지 않고 주어진 실행 파일에 대한 현재 실행 파일 (즉, 셸)을 변경합니다. 프로세스 ID (및 열린 파일 및 기타 항목)는 동일하게 유지됩니다. 에 반대로 eval,는 exec하지 않는 호출 쉘에 반환하지 않습니다 exec자체가 실패로 인해 찾거나 실행 파일을로드하거나 인수 확장 문제에 죽을 수없는 존재로.

eval기본적으로 인수 후 인수를 문자열로 해석합니다. 즉, 와일드 카드 확장 및 인수 분할의 추가 계층을 수행합니다. exec그런 일은하지 않습니다.


답변

평가

이 작품들 :

$ echo hi
hi

$ eval "echo hi"
hi

$ exec echo hi
hi

그러나 다음과 같은 것은 아닙니다.

$ exec "echo hi"
bash: exec: echo hi: not found

$ "echo hi"
bash: echo hi: command not found

공정 이미지 교체

이 예제 exec는 호출 프로세스의 이미지를 바꾸는 방법을 보여줍니다 .

# Get PID of current shell
sh$ echo $$
1234

# Enter a subshell with PID 5678
sh$ sh

# Check PID of subshell
sh-subshell$ echo $$
5678

# Run exec
sh-subshell$ exec echo $$
5678

# We are back in our original shell!
sh$ echo $$
1234

공지 사항 exec echo $$서브 쉘의 PID에 달렸다! 또한, 완성 된 후에 우리는 원래 sh$껍질 로 돌아 왔습니다 .

반면에 공정 이미지를 대체 eval하지 않습니다 . 오히려 일반적으로 쉘 자체에서와 같이 주어진 명령을 실행합니다. (물론, 프로세스가 생성되어야하는 명령을 실행하면 … 그냥 그렇게합니다!)

sh$ echo $$
1234

sh$ sh

sh-subshell$ echo $$
5678

sh-subshell$ eval echo $$
5678

# We are still in the subshell!
sh-subshell$ echo $$
5678