从 Bash 内置 exec 的文档中考虑这一点:
exec 替换 shell 而不创建新进程
请提供一个用例/实际示例。我不明白这有什么意义。
我用谷歌搜索并找到了关于I/O 重定向。你能解释得更好吗?
答案1
exec
经常在 shell 脚本中使用,主要充当启动其他二进制文件的包装器。例如:
#!/bin/sh
if stuff;
EXTRA_OPTIONS="-x -y -z"
else
EXTRA_OPTIONS="-a foo"
fi
exec /usr/local/bin/the.real.binary $EXTRA_OPTIONS "$@"
这样,在包装器完成运行后,“真正的”二进制文件将接管,并且不再有任何临时占用进程表中同一槽的包装器脚本的痕迹。 “真正的”二进制文件是启动它的任何东西的直接子代,而不是孙子。
您在问题中还提到了 I/O 重定向。这是一个完全不同的用例exec
,与用另一个进程替换 shell 无关。当exec
没有参数时,像这样:
exec 3>>/tmp/logfile
然后命令行上的 I/O 重定向在当前 shell 进程中生效,但当前 shell 进程继续运行并继续执行脚本中的下一个命令。
答案2
我使用了 shellexec
内置函数来获取 Java 程序的进程 ID (PID)。现在可能有一种方法可以从Java内部获取PID,但几年前还没有。一旦进程拥有自己的 PID,它就可以将其写入 PID 文件(查找/var/run/
带有“.pid”后缀的文件名),以允许管理程序了解正在运行的进程的 PID,并防止出现第二个实例同一服务器的运行。它的工作原理是这样的:
exec java -cp=YourServer.jar StartClass -p $$
main()
类方法中的代码StartClass
处理参数解析并可以找到自己的进程ID。
答案3
为了好玩,请在具有用户进程统计和限制的系统后台运行以下程序(翻译为您选择的实现语言)。
while(true) fork();
既然进程表中允许您使用的每个槽都充满了该正在运行的程序的副本,那么您打算如何杀死它呢?启动kill(1)需要另一个进程槽,但你没有。用kill命令替换shell本身肯定会很方便......
exec /bin/kill -9 -1
(假设您的系统在 /bin/kill 处有kill(1)。“exec`whichkill`-9 -1”可能更安全。)这会将SIGKILL发送到您可以发送的每个进程。
(注意:不要从启动的 shell 中注销,除非进程限制始终允许新登录其 shell 的进程槽。如果这样做,清理可能会有点困难。我当然没有在90 年代初期。)
答案4
这类似于 Bruce 需要知道进程 PID 的示例:
( cmdpid=$BASHPID; (sleep 300; Kill "$cmdpid") & exec长时间运行的命令)
其中你
- 启动一个子 shell(外部
(
和)
), - 找出子shell的PID。 (
$$
将为您提供主 shell 的 PID。) - 分离一个在超时后终止进程的子 shell,然后
- 在您在步骤 1 中创建的子 shell 的进程中运行命令。
这将运行
long-running-command
,但仅限于预先确定的有限时间。- 启动一个子 shell(外部
这有点无聊,但是,如果您决定在其余的登录会话中以 root 用户(或其他用户)身份进行操作,则可以
exec su
。实际上,我可以想象一个这确实有用的场景。假设您已登录到远程系统,并且由于某种原因,中断连接并启动新连接时出现问题。例如,假设远程系统有一个遵循时间表的防火墙。当您连接时,您被允许连接,并且已建立的连接不会关闭,但目前不接受新连接。
您已经完成了想做的事情,现在可以注销了。您的朋友鲍勃和您在一个房间里,他想在远程系统上做一些工作 - 但他无法连接。因此,您键入
exec su - bob
,然后在出现密码提示时将工作站移交给他。现在没有任何进程使用您的 UID(除非您在后台运行某些内容),因此 Bob 将无法乱搞您的文件。他将有效地接管您的连接(在您的同意和合作下)。笔记:
- 当然,如果你不被允许运行,这将不起作用
su
。 - 它将被记录,因此您可能需要向某人解释您的动机。由于您正在规避策略(防火墙计划),因此您可能会遇到麻烦。
- 我不保证它 100% 安全。例如,
who
可能仍会显示您的名字。可以想象,某些(写得不好的)程序会使用它来认为鲍勃是您,并让他访问您的资源。 - 如果系统进行审核,Bob 的操作可能会以您的名义进行审核。
- 当然,如果你不被允许运行,这将不起作用