“exec”命令有什么作用?

bec*_*cko 169 bash

我不明白 bash 命令exec。我已经看到它内部使用脚本来重定向所有输出到一个文件中(如被看见在)。但我不明白它是如何工作的或它的一般作用。我已经阅读了手册页,但我不明白它们。

fkr*_*iem 131

man bash 说:

exec [-cl] [-a name] [command [arguments]]
      If command is specified, it replaces the shell.  No new  process
      is  created.  The arguments become the arguments to command.  If
      the -l option is supplied,  the  shell  places  a  dash  at  the
      beginning  of  the  zeroth  argument passed to command.  This is
      what login(1) does.  The -c option causes command to be executed
      with  an empty environment.  If -a is supplied, the shell passes
      name as the zeroth argument to the executed command.  If command
      cannot  be  executed  for  some  reason, a non-interactive shell
      exits, unless the execfail shell option  is  enabled.   In  that
      case,  it returns failure.  An interactive shell returns failure
      if the file cannot be executed.  If command  is  not  specified,
      any  redirections  take  effect  in  the  current shell, and the
      return status is 0.  If there is a redirection error, the return
      status is 1.
Run Code Online (Sandbox Code Playgroud)

最后两行很重要:如果你自己运行exec,没有命令,它只会让重定向应用到当前的 shell。您可能知道,当您运行 时command > file, 的输出将command写入file而不是写入您的终端(这称为重定向)。如果您exec > file改为运行,则重定向适用于整个 shell:shell 产生的任何输出都将写入file您的终端,而不是写入您的终端。例如这里

bash-3.2$ bash
bash-3.2$ exec > file
bash-3.2$ date
bash-3.2$ exit
bash-3.2$ cat file
Thu 18 Sep 2014 23:56:25 CEST
Run Code Online (Sandbox Code Playgroud)

我首先启动一个新的bashshell。然后,在这个新的 shell 中,我运行exec > file,以便所有输出都重定向到file. 事实上,在那之后我运行date但我没有得到任何输出,因为输出被重定向到file. 然后我退出我的 shell(这样重定向不再适用),我看到它file确实包含date我之前运行的命令的输出。

  • 这只是部分解释。`exec` 还用于用命令替换当前的 shell 进程,以便父进程和子进程拥有 pid。这不仅用于重定向。请添加此信息 (70认同)
  • 跟进@SergiyKolodyazhnyy 的评论,我刚刚遇到的将我带到此页面的示例是 docker-entrypoint.sh,在对 nginx 配置执行各种操作后,脚本的最后一行是 `exec nginx <various nginx 参数>`。这意味着 nginx 接管了 bash 脚本的 pid,现在 nginx 是容器的主要运行进程,而不是脚本。我认为这只是为了清洁,除非其他人知道更具体的原因? (9认同)
  • @Luke 这称为“包装脚本”。“gnome-terminal”也是一个例子,它至少在 14.04 有一个包装脚本来设置参数。这就是他们唯一的目的,真的 - 设置艺术和环境。另一种情况是清理 - 首先杀死进程的先前实例并启动新的 (3认同)
  • @LukeGriffiths,容器启动脚本中`exec` 的主要原因是PID 1,容器的入口点,在Docker 中具有特殊意义。它是一个接收信号的主进程,当它存在时,容器也退出。`exec` 只是一种将 `sh` 从这个命令链中取出的方法,并使守护进程成为容器的主进程。 (2认同)

jll*_*gre 86

exec 是一个具有两种截然不同行为的命令,取决于是否至少使用了一个参数,或者根本不使用参数。

  • 如果至少传递了一个参数,则将第一个参数作为命令名称并exec尝试将其作为命令执行,将其余参数(如果有)传递给该命令并管理重定向(如果有)。

  • 如果作为第一个参数传递的命令不存在,则当前 shell(不仅是exec命令)会错误退出,除非 shell 是交互式的或execfail设置了 bash 选项( shopt -s execfail)。另见https://superuser.com/questions/992204/why-does-exec-non-existent-file-exits-the-shell-when-in-a-script-that-is-sourc

  • 如果该命令存在且可执行,则它会替换当前的 shell。这意味着如果exec出现在脚本中,则永远不会执行exec 调用后面的指令(除非exec它本身在子外壳中)。成功者exec永不回头。也不会触发诸如“EXIT”之类的 Shell 陷阱。

  • 如果不传递参数,exec则仅用于重新定义当前的 shell 文件描述符。exec与前一种情况不同,shell 在 之后继续,但标准输入、输出、错误或任何已重定向的文件描述符都会生效。

  • 如果某些重定向使用/dev/null,则来自它的任何输入都将返回 EOF 并且任何输出都将被丢弃。

  • 您可以使用-作为源或目标来关闭文件描述符,例如exec <&-. 随后的读取或写入将失败。

这里有两个例子:

echo foo > /tmp/bar
exec < /tmp/bar # exec has no arguments, will only affect current shell descriptors, here stdin
cat # simple command that read stdin and write it to stdout
Run Code Online (Sandbox Code Playgroud)

此脚本将输出“foo”作为 cat 命令,而不是像通常情况下那样等待用户输入,而是从包含 foo 的 /tmp/bar 文件中获取输入。

echo foo > /tmp/bar
exec wc -c < /tmp/bar # exec has two arguments, the control flow will switch to the wc command
cat
Run Code Online (Sandbox Code Playgroud)

此脚本将显示4(/tmp/bar 中的字节数)并立即结束。该cat命令将不会被执行。

  • `一些旧帖子永远不会变老...... +1。 (7认同)
  • *如果某些重定向使用/dev/null,则关联的文件描述符被关闭。* 不,它确实重定向到/从`/dev/null`,因此写入仍然成功并且读取返回EOF。fd 上的 `close(2)` 会导致读/写系统调用返回错误,例如你可以使用 `exec 2&gt;&amp;-` 来实现。 (4认同)
  • 这个答案很棒,因为它解释了 `exec` 的 **两个** 用例,当前投票最多的答案只讨论了一个用例,而 g_p 的另一个答案只讨论了另一个用例。对于如此复杂的主题,这个答案很好且简洁/可读。 (4认同)
  • 自己试试: `exec 3&lt;/dev/null;` `ls -l /proc/self/fd`:注意 fd 3 将在 /dev/null 上以只读方式打开。然后再次用`exec 3&lt;&amp;-`关闭它,你可以看到(再次用`ls -l /proc/$$/fd`)你的shell进程不再有fd 3了。(用`exec &lt;&amp;-` 关闭stdin 在脚本中很有用,但在交互上它是一个注销。) (3认同)

g_p*_*g_p 44

要理解exec你首先需要理解fork。我试图保持简短。

  • 当您走到岔路口时,您通常有两种选择。Linux 程序在遇到fork()系统调用时会到达这个岔路口 。

  • 普通程序是以编译形式存在于系统中的系统命令。当执行这样的程序时,会创建一个新进程。该子进程与其父进程具有相同的环境,只是进程 ID 号不同。此过程称为 分叉

  • Forking 为现有进程提供了一种启动新进程的方法。但是,可能存在子进程与父进程不属于同一程序的情况。在这种情况下exec使用。exec 将用程序二进制文件中的信息替换当前正在运行的进程的内容。
  • 在 fork 进程之后,子进程的地址空间被新的进程数据覆盖。这是通过对系统的 exec 调用来完成的。

  • 我发现这不清楚“但是,可能存在子进程与父进程不属于同一程序的一部分的情况” (4认同)
  • 你能解释一下为什么 `exec` 可以重定向脚本输出,就像我发布的链接一样吗? (3认同)

mur*_*uru 10

bash,如果你这样做help exec

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

    Execute COMMAND, replacing this shell with the specified program.
    ARGUMENTS become the arguments to COMMAND.  If COMMAND is not specified,
    any redirections take effect in the current shell.

    Options:
      -a name   pass NAME as the zeroth argument to COMMAND
      -c        execute COMMAND with an empty environment
      -l        place a dash in the zeroth argument to COMMAND

    If the command cannot be executed, a non-interactive shell exits, unless
    the shell option `execfail' is set.

    Exit Status:
    Returns success unless COMMAND is not found or a redirection error occurs.
Run Code Online (Sandbox Code Playgroud)

相关位:

If COMMAND is not specified, any redirections take effect in the current shell.
Run Code Online (Sandbox Code Playgroud)

exec是一个shell内建,这是壳等效的exec家庭的系统调用G_P讲的(和它的手册页,你似乎有读)。如果没有指定命令,它只具有影响当前 shell的POSIX 强制功能。