shell脚本中exec命令的用途是什么?


249

谁能用简单的例子解释exec命令在shell脚本中的用途吗?


31
指向linux.about.com的链接没有帮助。他们没有清楚地表明使用'exec'执行命令或命令管道与仅正常执行命令有何不同。因此,OP的问题是“ exec的用途是什么?”?
乔纳森·哈特利2014年

2
堆栈溢出是一个用于编程和开发问题的站点。这个问题似乎与主题无关,因为它与编程或开发无关。请在帮助中心中查看我可以询问哪些主题。也许超级用户Unix&Linux Stack Exchange是更好的选择。
jww

Answers:


281

exec内核中的内置命令镜功能,还有他们家的基础上execve,这通常是由被称为C.

exec在当前进程中替换当前程序,而无需fork执行新进程。并不是您在编写的每个脚本中都会使用它,但有时会派上用场。这是我使用过的一些场景;

  1. 我们希望用户在不访问外壳的情况下运行特定的应用程序。我们可以在/ etc / passwd中更改登录程序,但是也许我们希望从启动文件中使用环境设置。因此,在(say)中.profile,最后一条语句表示如下内容:

     exec appln-program

    所以现在没有外壳可以回去了。即使appln-program崩溃,最终用户也无法进入shell,因为它不存在-被exec替换了。

  2. 我们想使用与/ etc / passwd中不同的shell。看起来很愚蠢,有些网站不允许用户更改其登录外壳。我知道有一个站点,每个人都从这里开始csh,每个人都.login将对的调用放入(csh启动文件)ksh。在此csh过程中,它留下了一个混乱的进程,并且注销是两个阶段,可能会造成混乱。因此,我们将其更改为exec ksh仅用korn shell替换了c-shell程序,并使一切变得更简单(与此相关的还有其他问题,例如,事实ksh并非登录shell)。

  3. 只是为了节省流程。如果我们打电话prog1 -> prog2 -> prog3 -> prog4等,再也不回头,那么让每个电话都执行一次。这样可以节省资源(当然,除非重复,否则可以节省很多资源),并且使关机更加简单。

您显然已经exec在某处使用过,也许如果您显示的代码有问题,我们可以证明其使用是合理的。

编辑:我意识到我上面的答案是不完整的。in shell 有两种用法,exec例如kshbash-用于打开文件描述符。这里有些例子:

exec 3< thisfile          # open "thisfile" for reading on file descriptor 3
exec 4> thatfile          # open "thatfile" for writing on file descriptor 4
exec 8<> tother           # open "tother" for reading and writing on fd 8
exec 6>> other            # open "other" for appending on file descriptor 6
exec 5<&0                 # copy read file descriptor 0 onto file descriptor 5
exec 7>&4                 # copy write file descriptor 4 onto 7
exec 3<&-                 # close the read file descriptor 3
exec 6>&-                 # close the write file descriptor 6

请注意,间距在这里非常重要。如果在fd数字和重定向符号之间放置空格,则将exec恢复为原始含义:

  exec 3 < thisfile       # oops, overwrite the current program with command "3"

有几种方法可以使用它们,例如,在ksh read -uprint -u上使用bash,例如:

read <&3
echo stuff >&4

22
这里还有另一种用法,我发现很方便,这里值得一提,因为它涉及到Python的Web应用程序,似乎介于两者之间的(真棒)回答2和3。我通常运行WSGI的Web应用程序(比如Django的或烧瓶)到主管调用gunicorn应用程序:后者臭名昭著地需要大量环境变量,它是通过shell脚本运行起来更容易,更可维护的,它可以通过shell脚本来设置所有内容,并最终exec gunicorn将正确的pid返回给主管。
gru 2015年

1
该文档不说,exec可用于重定向:>如果没有指定命令,任何重定向采取在当前shell效果,返回状态是0。如果有一个重定向错误,返回状态是1。但如何exec实际工作来更改文件描述符?为什么为此任务选择此特定命令?(Markdown现在失败了吗?)

@Ray:尽我所能:pubs.opengroup.org/onlinepubs/009604599/utilities/exec.html 。如果您需要了解方法,请查看shell源代码。
cdarke

7
另一个用途:将所有脚本的输出重定向到日志文件exec >.\logfilename.log 2>&1
Hogan

1
@Carmageddon: &>bash扩展名(请参阅man bash),您的示例等效于exec >/var/log/userdata.log 2>&1。换句话说,它将stdout和stderr重定向到该文件。除非重置它们,否则后面的命令将继承这些重定向,但是将执行它们。
cdarke

41

只需使用简短的新手友好的简短答案来增加已接受的答案,您可能就不需要exec

如果您仍在这里,则以下讨论将有望揭示原因。跑步时说

sh -c 'command'

您运行一个sh实例,然后从commandsh实例的子级开始。当command完成后,该sh实例也结束。

sh -c 'exec command'

运行一个sh实例,然后二进制文件替换sh实例command,然后运行它。

当然,在这种有限的情况下,这两者都是无用的。你只是想要

command

在某些情况下,您希望shell读取其配置文件,或者以其他方式设置环境以作为运行准备command。这几乎是唯一exec command有用的情况。

#!/bin/sh
ENVIRONMENT=$(some complex task)
exec command

这会做一些事情来准备环境,使其包含所需的内容。一旦完成,sh就不再需要该实例,因此,这是一个(次要)优化,只需sh用该command流程替换该实例,而不是将sh其作为子进程运行并等待它,然后在完成时退出即可。

同样,如果您想在shell脚本的末尾释放尽可能多的资源来执行繁琐的命令,则可能需要exec将该命令作为优化。

如果某些事情迫使您运行,sh但您确实想运行其他东西,exec something else则当然是一种替代方法,用于替换不需要的sh实例(例如,如果您确实想运行自己的spiffy gosh而不是您的spiffy ,sh但您的未列出),则/etc/shells可以不要将其指定为您的登录外壳)。

exec操纵文件描述符的第二个用途是一个单独的主题。公认的答案很好地涵盖了这一点。为了使它自成一体,对于任何exec后面跟着重定向而不是命令名的内容,我都只会参考手册。


2
抵制诱惑来调用自己的Spiffy Shell bush。当然,这也适用于bash或通常适用于任何流行的Unix shell。尽管正如@anishsane所指出的,Bash bash -c 'command'通过实际进行暗中优化exec command
三胞胎

1
Shell的退出代码通常是它执行的最后一个命令的退出代码。当然,如果外壳无法运行或无法执行命令,则外壳的退出状态将以适当的错误代码反映出来。
三胞胎

2
@JonathanLeffler我正在故意记录一个反模式,我在新手问题中都会看到它。此副本由此副本提示的,但我以前见过,所以我想专门介绍一下。
三点

2
@JonathanLeffler次要措辞调整;您认为这足够了吗?感谢您的反馈。
点钟

2
谢谢-我添加了关于此优化的另一段简短内容。如果您还有其他异议,或者您只是想以不同的角度来解释,对我来说还不是很清楚。
点钟
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.