eval
그리고 exec
명령을 실행하는 bash (1) 명령이 내장되어 있습니다.
또한 exec
몇 가지 옵션이 있지만 유일한 차이점은 무엇입니까? 그들의 상황은 어떻게됩니까?
eval
그리고 exec
명령을 실행하는 bash (1) 명령이 내장되어 있습니다.
또한 exec
몇 가지 옵션이 있지만 유일한 차이점은 무엇입니까? 그들의 상황은 어떻게됩니까?
답변:
eval
와 exec
완전히 다른 짐승이다. (둘 다 명령을 실행하지만 쉘에서 수행하는 모든 작업을 수행한다는 사실을 제외하고)
$ 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/self
은 ls
쉘에서 실행 된 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 $HOME
running eval "$cmd"
은 원하지 않는 일입니다. 그래서 문자열 내부 명령 대체처럼 심지어 일들이 처리 될 사람이해야 정말 입력이 할 수 있는지 확인 eval
을 사용하기 전에 안전합니다.
종종 eval
실수로 코드와 데이터를 잘못 혼합하는 것을 피하고 피할 수 있습니다.
eval
것에 대한 일반적인 면책 조항을 추가하도록 상기시킵니다 . 간접적으로 수정 변수와 같은 물건을 통해 많은 포탄으로 수행 할 수 있습니다 declare
/ typeset
/ nameref
추천하고 확장 ${!var}
, 그래서 내가 대신 그 사용하는 것이 eval
정말 그것을 피할했다 않는합니다.
exec
새로운 프로세스를 만들지 않습니다. 그것은 대체하는 새로운 명령을 사용하여 현재 프로세스를. 명령 행에서이 작업을 수행하면 셸 세션이 효과적으로 종료됩니다 (그리고 로그 아웃하거나 터미널 창을 닫을 수도 있습니다).
예 :
ksh% bash
bash-4.2$ exec /bin/echo hello
hello
ksh%
여기에 있습니다 ksh
(일반 쉘). 나는 bash
bash를 시작 하고 시작한다 exec /bin/echo
. 프로세스가로 교체 되었으므로 ksh
나중에 다시 탈퇴 한 것을 확인할 수 있습니다 .bash
/bin/echo
exec
명령이 지정되지 않은 경우 현재 쉘 프로세스를 새 것으로 대체하고 스트림 경로 재 지정 / 파일 디스크립터를 처리하는 데 사용됩니다. eval
문자열을 명령으로 평가하는 데 사용됩니다. 둘 다 런타임에 알려진 인수로 명령을 빌드하고 실행하는 데 사용될 수 있지만 exec
명령을 실행하는 것 외에도 현재 쉘의 프로세스를 대체합니다.
통사론:
exec [-cl] [-a name] [command [arguments]]
이 내장 명령이 지정된 경우 매뉴얼에 따라
... 쉘을 대체합니다. 새로운 프로세스가 생성되지 않습니다. 인수는 명령에 대한 인수가됩니다.
다시 말해, bash
PID 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
합니다.
간단히 말해서 exec
C 프로그래밍 언어로 된 명령 계열이 shell_execve()
있으며 기본적으로 다음과 같은 래퍼 함수입니다 execve
.
/* Call execve (), handling interpreting shell scripts, and handling
exec failures. */
int
shell_execve (command, args, env)
char *command;
char **args, **env;
{
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
exec
)