evalexec都是内置在执行命令的bash(1)命令中。他们的上下文发生了什么?

评论

相关的askubuntu.com/questions/525767/what-does-an-exec-command-do / ...

相关unix.stackexchange.com/a/279030/5510

#1 楼

evalexec是完全不同的野兽。 (除了两者都将运行命令的事实之外,您在shell中所做的一切也是如此。)

exec cmd,除了用命令代替当前外壳程序,而不是运行单独的进程。在内部,运行说cmd将调用/bin/ls创建一个子进程,然后在该子进程中调用fork()执行exec()。另一方面,/bin/ls不会分叉,而只是替换外壳。

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


exec /bin/ls打印我启动的外壳的PID,列出echo $$会给我们从外壳运行的/proc/self的PID。通常,进程ID是不同的,但是对于ls,外壳程序和exec具有相同的进程ID。另外,由于替换了外壳,ls之后的命令也未运行。


另一方面: > exec将在当前Shell中将参数作为命令运行。换句话说,evaleval foo bar相同。但是变量将在执行之前被扩展,因此我们可以执行保存在shell变量中的命令:当前的外壳。 (当然,foo bar将创建一个子进程,就像普通的旧eval /bin/ls一样。)

或者我们可以有一个输出shell命令的命令。运行/bin/ls在后台启动代理,并输出一堆变量赋值,这些赋值可以在当前shell中设置并由子进程使用(您将运行的ssh-agent命令)。因此,ssh可以使用以下命令开始:当然,如果变量ssh-agent恰好包含类似cmd的内容,那么运行rm -rf $HOME并不是您想要的。甚至可以处理字符串中的命令替换之类的事情,因此,在使用它之前,请务必确保eval "$cmd"的输入是安全的。

通常,可以避免eval甚至避免意外地混合代码和数据以错误的方式。

评论


这是一个很好的答案,@ ilkkachu。谢谢!

– Willian Paixao
16年7月19日在16:46

有关使用eval的更多详细信息,请参见:stackoverflow.com/a/46944004/2079103

–clearlight
17年11月14日15:06



@clearlight,好吧,这使我想起在此答案中也添加了关于不首先使用eval的通常免责声明。诸如间接修改变量之类的东西可以在许多shell中通过clarify / typeset / nameref和$ {!var}之类的扩展来完成,因此除非真正避免使用,否则我将使用那些代替eval。

–ilkkachu
17年11月14日在18:02

+1是一个很好的解释,重点放在一些要点上。谢谢!

– bitfox
20 Mar 20 '20 at 19:27

#2 楼

exec不创建新进程。它用新命令替换当前进程。如果您在命令行上执行了此操作,那么它将有效地结束您的Shell会话(并可能注销您或关闭终端窗口!)。 >
我在这里(我的普通外壳)在ksh中。我先启动bash,然后在bash中启动exec /bin/echo。我们可以看到,之后我又回到了ksh,因为bash进程已被/bin/echo取代。

评论


脸上的umm掉回了ksh b / c进程,被echo代替了,没有太大意义吗?

–user282164
19年5月31日,3:35

#3 楼

TL; DR

exec用于用新命令替换当前的Shell进程,并在未指定命令的情况下处理流重定向/文件描述符。 eval用于将字符串作为命令求值。两者都可以用来建立和执行带有运行时已知参数的命令,但是exec除了执行命令外,还替换了当前Shell的进程。
br语法:

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


如果手册中指定了此内置


...替换外壳。没有创建新的过程。自变量成为命令的自变量。换句话说,如果您正在运行带有PID 1234的bash,并且要在该shell中运行exec top -u root,则top命令将具有PID 1234并替换您的shell进程。

这在哪里有用?在称为包装器脚本的东西中。这样的脚本会建立参数集或对将哪些变量传递到环境中做出某些决定,然后使用exec用指定的任何命令替换自身,当然还要提供包装脚本在创建过程中建立的相同参数。

该手册还指出:


如果未指定命令,则任何重定向都将在当前shell中生效


这使我们可以将当前Shell输出流中的所有内容重定向到文件中。这对于日志或过滤目的可能是有用的,您不想看到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


此行为使登录shell脚本,将流重定向到单独的文件或进程以及其他带有文件描述符的有趣东西变得非常方便。

至少在bash版本4.3的源代码级别上,内置的execbuiltins/exec.def中定义。它解析收到的命令,如果有命令,则将其传递给shell_execve()文件中定义的execute_cmd.c函数。

长话短说,有C语言的exec命令家族,而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手动状态(我加了重点):然后,shell将读取并执行此命令,并且退出状态将作为eval的值返回。


请注意,没有进程替换发生。与exec的目标是模拟execve()的功能不同,内置的eval仅用于“评估”参数,就像用户在命令行上键入参数一样。这样,就会创建新的流程。

在哪里有用?正如Gilles在此答案中指出的那样:“ ... eval很少使用。在某些shell中,最常见的用法是获取名称直到运行时才知道的变量的值。就个人而言,我在Ubuntu上的几个脚本中使用了该脚本,其中有必要根据用户当前使用的特定工作区执行/评估命令。

在源代码级别上,它是在builtins/eval.def中定义的,并将已解析的输入字符串传递给evalstring()函数。

eval可以分配保留在当前shell执行环境中的变量,而exec不能:

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


评论


@ ctrl-alt-delor我已经编辑了那部分,谢谢,尽管可以说它确实产生了一个新进程,但PID仍然与当前shell相同。以后考虑编辑答案,而不要留下评论和低票,尤其是在对诸如7词短语这样的小问题有疑问的情况下;修改和更正答案的时间要少得多,而且也很有帮助。

– Sergiy Kolodyazhnyy
19年5月5日在17:02

#4 楼

创建一个新的子进程,运行参数并返回退出状态。

嗯? eval的全部要点是它不会以任何方式创建子进程。如果我在shell中执行

eval "cd /tmp"


,那么当前的shell将更改目录。 exec都不会创建新的子进程,而是会为给定的子进程更改当前的可执行文件(即外壳程序)。进程ID(以及打开的文件和其他内容)保持不变。与eval相反,除非exec本身由于找不到或加载可执行文件或死于参数扩展问题而失败,否则它不会返回到调用shell。 s)作为连接后的字符串,即它将做一层额外的通配符扩展和参数拆分。 exec不会做类似的事情。

评论


最初的问题是“ eval和exec都是Bash(1)的内置命令,它们执行命令,创建新的子进程,运行参数并返回退出状态”;我已经排除了错误的推定。

–查尔斯·斯图尔特
16年7月20日在15:22

#5 楼

评估

这些工作:

$ echo hi
hi

$ eval "echo hi"
hi

$ exec echo hi
hi


但是,这些却没有: />
过程映像替换

此示例演示exec如何替换其调用过程的映像:用子shell的PID运行!此外,完成后,我们回到了原来的exec echo $$外壳中。

另一方面,sh$不会替换过程映像。相反,它会像通常在外壳本身中一样运行给定命令。 (当然,如果您运行的命令需要生成一个进程...它就是这样做的!)

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

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


评论


我发布了此答案,因为对于像I这样的虚弱头脑,其他示例并未真正以充分理解的方式说明了这一点。

–玛蒂·乌尔哈克(Mateen Ulhaq)
19年7月29日在3:58



词语选择不当:流程替换是一个现有概念,似乎与您在此处说明的内容无关。

–muru
19年7月29日在4:33

@muru“流程替换”更好吗?我不确定正确的术语是什么。该联机帮助页似乎将其称为“替换过程映像”。

–玛蒂·乌尔哈克(Mateen Ulhaq)
19年7月29日在4:38

嗯,不知道(但是,当我听到“替换过程映像”时,我认为:exec)

–muru
19年7月29日在5:04