你真的了解 bash 的 exec 命令吗?

2023-05-16

前言

bash exec 命令不太常用,我对它提供的功能也仅知一二,没有掌握全貌。最近遇到了几个问题,从问题出发研究了一通找到 exec 命令头上,问题迎刃而解后,才对 exec 命令提供的功能有了进一步的认识。

在本文中,我将从 bash manual 中 exec 命令的帮助信息出发,完整的描述 exec 命令提供的功能以及我过去几个月遇到的两个与 exec 命令相关的问题。

exec 函数的 manual 信息

       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 shell option execfail is enabled, in which 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.

exec 命令提供的基本功能加载新的程序替换 shell 并执行,除此之外它还有如下几个重要的功能:

  1. -l 参数,在命令的第 0 个参数前面添加一个 -
  2. -a 参数,将命令的第 0 个参数替换为指定的名称
  3. -c 参数,使用一个空的环境执行程序

下面我分别对这三个功能进行解释。

1. exec -l

-l 选项在命令的第 0 个参数前面添加一个 -,这一功能不常用。这里添加的 - 与 login shell 有关。

在 bash 登录时读配置文件问题 这篇文章中,我描述过 login shell 与交互式 shell 初始化过程中读配置文件的区别。

login shell 的定义如下:

A login shell is one whose first character of argument zero is a -, or one started with the --login option.

当执行 shell 程序的第 0 个参数的第一个字符为 - 时,此 shell 为 login shell,login shell 执行的时候会读取 ~/.bash_profile 文件。

使用如下命令进行测试:

[longyu@debian-10:19:21:18] ~ $ echo "echo test" >> ~/.bash_profile
[longyu@debian-10:19:21:25] ~ $ exec -l bash
test
[longyu@debian-10:19:21:38] ~ $ exec bash
[longyu@debian-10:19:21:47] ~ $ 

上述命令首先在 ~/.bash_profile 文件中追加一行打印,然后执行 exec -l bash 命令,执行后可以看到终端打印了 test 字符串,表明 ~/.bash_profile 文件被加载执行。这之后执行 exec bash 命令,终端没有任何输出,这时执行的 shell 不是 login shell 故而不读取 ~/.bash_profile 文件内容。

2. exec -a

-a 参数可以称为 -l 参数的扩展,它能够直接替换第 0 个参数的内容。使用如下脚本进行测试:

#!/bin/bash

exec -a test ls

使用 strace 跟踪执行并在输出中搜索 execve 系统调用,得到了如下信息:

[longyu@debian-10:19:27:14] ~ $ strace ./test.sh 2>&1 | grep execve
execve("./test.sh", ["./test.sh"], 0x7ffc3d432a10 /* 73 vars */) = 0
execve("/usr/bin/ls", ["test"], 0x55782a206150 /* 73 vars */) = 0

第一次系统调用执行了 ./test.sh 脚本,第二次系统调用执行了 /usr/bin/ls 程序,且程序的第 0 个参数被替换为了 test。

3. exec -c

-c 选项将会在空的环境中执行程序,这里说的环境指的是环境变量

在一个终端中执行如下命令在空环境中执行 awk 程序:

[longyu@debian-10:19:36:56] ~ $ exec -c awk '{print $1}'

在另外一个终端中查看 awk 命令的环境变量:

[longyu@debian-10:19:37:25] ~ $ ps aux | grep awk
longyu   19374  0.0  0.0  10180  2500 pts/2    Ss+  19:36   0:00 awk {print $1}
longyu   19425  0.0  0.0  18976   884 pts/3    S+   19:38   0:00 grep --color=auto awk
[longyu@debian-10:19:38:14] ~ $ cat /proc/19374/environ 
[longyu@debian-10:19:38:21] ~ $ 

可以确认环境变量为空

在一个终端中正常执行 awk 程序:

[longyu@debian-10:19:38:40] ~ $ awk '{print $1}'

在另外一个终端中查看 awk 程序的环境变量内容:

[longyu@debian-10:19:38:49] ~ $ ps aux | grep awk
longyu   19464  0.0  0.0  26064  3052 pts/3    S+   19:38   0:00 awk {print $1}
longyu   19483  0.0  0.0  18976   888 pts/2    S+   19:38   0:00 grep --color=auto awk
[longyu@debian-10:19:38:53] ~ $ cat /proc/19464/environ 
SHELL=/bin/bashSESSION_MANAGER=local/debian-10:@/tmp/.ICE-unix/1891,unix/debian-10:/tmp/.ICE-unix/1891QT_ACCESSIBILITY=1COLORTERM=truecolorXDG_MENU_PREFIX=gnome-GNOME_DESKTOP_SESSION_ID=this-is-deprecatedGTK_IM_MODULE=fcitxHISTSIZE=-1QT4_IM_MODULE=fcitxLC_ADDRESS=zh_CN.UTF-8JAVA_HOME=/usr/local/jdk1.7.0_67LC_NAME=zh_CN.UTF-8SSH_AUTH_SOCK=/run/user/1000/keyring/ssh_ZL_MATCH_MODE=1XMODIFIERS=@im=fcitxDESKTOP_SESSION=gnome-xorgLC_MONETARY=zh_CN.UTF-8SSH_AGENT_PID=1940GTK_MODULES=gail:atk-bridgeXDG_SEAT=seat0PWD=/home

可以看到此时 awk 的环境变量有许多,这些环境变量是从父进程(bash)中继承到的!

在某些特定场景中,需要避免从父进程继承环境变量对子进程执行的影响,这时候 -c 参数就能够派上用场。

exec 与文件描述符的关闭问题

bash 的 exec 命令最终将会调用到 execve 系统调用,execve 系统调用在执行的时候将会关闭设置 FD_CLOEXEC 标志的文件描述符。有这样的认识后,当我遇到 为啥 bash 没有按照我想象的样子执行脚本呢? 这篇博文中描述的问题时,我想到了 exec,测试确定能够解决问题。

exec -a 修改命令的第一个参数问题

常见发行版中的 poweroff、reboot 命令都是个软链接,指向 systemctl 命令,与之类似的有 insmod、modinfo 命令,这两个命令也是个软链接,指向 kmod 命令。

以 reboot 为例,其命令信息如下:

[longyu@debian-10:19:59:13] ~ $ ls -lh /usr/sbin/reboot
lrwxrwxrwx 1 root root 14 1月  29 22:16 /usr/sbin/reboot -> /bin/systemctl

能够看到它指向了 systemctl 命令。

现在你需要在 reboot 命令执行前做某些操作,你不能添加一个新的命令,只能通过修改原有的 reboot 命令来实现,并且基于可维护性考虑你必须使用 bash 脚本来实现。

你可能会立马写出如下脚本:

#!/bin/bash

export PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin

# do something
systemctl reboot $@

执行 reboot -f 命令能够成功,然而当你尝试指定其它选项时却发现了问题。

例如你尝试执行 --help 选项时,终端却输出了 systemctl 的帮助信息,部分内容如下:

[longyu@debian-10:20:06:20] ~ $ ./reboot --help
systemctl [OPTIONS...] {COMMAND} ...

Query or send control commands to the systemd manager.

你期望的内容如下:

reboot [OPTIONS...] [ARG]

Reboot the system.

     --help      Show this help
     --halt      Halt the machine
  -p --poweroff  Switch off the machine
     --reboot    Reboot the machine
  -f --force     Force immediate halt/power-off/reboot
  -w --wtmp-only Don't halt/power-off/reboot, just write wtmp record
  -d --no-wtmp   Don't write wtmp record
     --no-wall   Don't send wall message before halt/power-off/reboot

See the halt(8) man page for details.

你首先会想为什么 systemctl 不支持 systemctl reboot --help 这种参数呢?这与 systemctl 命令的实现有关。

上文我已经说过了 reboot 与 poweroff 都是个链接文件且都指向了 systemctl 命令,当你执行 reboot 命令的时候第 0 个参数内容为 reboot,执行的时候却由于 execve 会 follows symbolic link,最终执行到 systemctl 命令systemctl 命令会判断第 0 个参数,执行不同的功能。

如果你想让你的脚本完全兼容 reboot 命令的参数,你需要做的就是替换执行命令的 argv[0],将它改为 reboot。按照这个思路搜索互联网,很快就能发现 exec -a 就能够提供这一功能。

修改后的脚本内容如下:

#!/bin/bash

export PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin

# do something
exec -a reboot systemctl $@

至此,问题得到解决!

总结

exec 命令看似简单却也不太简单,它充其量也不过是 bash 的冰山一角。我可以通过阅读 bash 命令的手册来确定 exec 命令支持的功能,这纯粹是记忆的过程且很容易遗忘。

我也可以从实际的问题出发,通过分析明确解决问题需要什么知识或功能,再去寻找这些知识与功能的实例,在这里 exec 就是满足上文描述的两个问题功能的实例。

参考链接

option -l of exec shell command

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

你真的了解 bash 的 exec 命令吗? 的相关文章

随机推荐

  • Choco-slover的使用

    一 相关资料 choco slover github源代码以及工具下载网址 https github com chocoteam choco solver choco slover 官网文档 https choco solver org c
  • python 进入虚拟环境 source activate 时候报错 Badly placed ()'s

    当想要进入python的指定虚拟环境 venv bin 时使用 source activate报错 source activate Badly placed 39 s 因为当前Linux使用的是 csh 或者fish shell时是无法使用
  • IDEA javadoc快捷键

    思悠悠 xff0c 恨悠悠 xff0c 恨到归时方始休 月明人依楼 白居易 长相思 汴水流 设置快捷键FIle gt Setting gt Keymap gt Other 搜索 Fix doc xff0c 右键 xff1a Add Keyb
  • 常见的关系型数据库(最全收录)

    天子呼来不上船 xff0c 自称臣是酒中仙 杜甫 饮中八仙歌 1 OracleOracle是1983年推出的世界上第一个开放式商品化关系型数据库管理系统 它采用标准的SQL结构化查询语言 xff0c 支持多种数据类型 xff0c 提供面向对
  • Docker报错,(default) No default Boot2Docker ISO found locally, downloading the latest release...

    环境简述 xff1a 系统 xff1a Windows 10家庭版 安装 Hyper V 后 xff0c 安装 Docker For Windows 还是不好使 后面选择下载了 xff0c Docker Toolbox xff0c 一路 n
  • golang学习笔记 casbin授权库学习记录

    目录 权限控制管理模型ACLRBACABAC Casbin介绍Casbin概述Casbin工作原理请求策略匹配器效果 Casbin结合golang基本使用添加policy结合gorm适配器使用对policy经行增删改查 自定义匹配函数 权限
  • 【洛谷】P1626象棋比赛

    题目描述 有 span class hljs keyword N span 个人要参加国际象棋比赛 xff0c 该比赛要进行 span class hljs keyword K span 场对弈 每个人最多参加两场对弈 xff0c 最少参加
  • 四、SLAM学习笔记—— Ubuntu18.04交叉编译OpenCV4.5.5至ARM64位平台firefly-RK3566

    概述 交叉编译大概的意思是指 在PC x86 x64 的机器上编译其他平台如ARM平台的代码 xff0c 这个过程叫做交叉编译 对于交叉编译 xff0c 我们首先确定目标平台的架构 xff0c RK3566的架构是ARMv8 xff0c 6
  • 关于金蝶组件各类报错问题

    最近维护金蝶K3wish xff0c 遇到个别客户端的组件报错问题 xff0c 现整理问题解决方法 xff1a 1 打开金蝶的追踪程序 xff0c 在本机金蝶安装盘下搜索 xff1a KdMainDBG exe程序 xff1b 2 加载进入
  • Charles + Postern 安卓模拟器抓包

    首先 xff0c 要注意的是 xff0c 在安卓7 0以后 xff0c Fiddle和charles这些抓包工具安装的证书 xff0c 会被默认不相信 xff0c 所以按照很多旧的抓包工具的配置过程 xff0c 没有办法成功抓包 今天就在这
  • HTML(一天学完)总结。附上html思维导图

    3 24 偷懒了几天 昨天加今天看完了狂神说的html 因为html和mysql是一起学的 xff0c 我看腻了mysql就去看html 在这里写一篇总结 附上html思维导图 HTML Hyper Text Markup Language
  • TGP漏洞:lol无法正常登录问题

    今天下午发现了个有趣的现象 xff0c 本来是要设置路由器的 xff0c 结果路由器设置好后 xff0c 上网之类的都正常 但就是玩不了lol xff0c 登进去总是提示与服务器无法连接 xff0c 您的网络有问题 好郁闷啊 xff0c 我
  • 代码参数里的 payload 是什么意思???

    首先解释一下什么是 payload payload 字面意思 有效载荷 xff0c 有效负荷 xff0c 有效载重 payload 维基百科payload中的解释 xff1a 在计算机科学与电信领域 xff0c 负载 xff08 英语 xf
  • 树莓派 Volumio 3.5触摸屏(GPIO) 配置教程

    最近咸鱼淘了一个树莓派3B 买成210血亏哦 200以下差不多 做数播 装的Volumio系统 和少帅的i2c转同轴的板 连同壳子140 43 后来又淘了一个3 5寸的TFT小屏幕3 5inch RPi Display LCD wiki 我
  • Ubuntu 16.04 LTS 一键安装VNC

    Ubuntu 16 04 LTS 安装VNC xff0c 在百度和谷歌找了很多教程 xff0c 不是太老 xff0c 就是说的驴唇不对马嘴 xff0c 所以忍不住写一些以正视听 Ubuntu 16 04 LTS是最近出的LTS版本系统 xf
  • OpenMP简介

    01 OpenMP简介 OpenMP xff08 开放式多处理 xff09 是一种应用程序编程接口 xff08 API xff09 xff0c 支持C xff0c C 43 43 和Fortran中的多平台共享内存 多 处 理
  • windows10 ubuntu18.04 CUDA9.2 caffe tensorflow pytorch安装

    文章目录 windows10 安装ubuntu18 04 系统安装1 制作U盘启动盘2 更改英文路径3 更新源4 设置root账号5 安装NVIDIA驱动6 CUDA安装7 CUDNN安装8 Anaconda安装9 Pytorch安装10
  • 场景分类资料

    1 https blog csdn net Wayne2019 article details 78629204 2 场景分类 xff08 1 xff09 研究方法 一 两个步骤 xff08 一 xff09 从图像中获得表现图像场景的图像特
  • ubuntu如何降级到之前的版本

    找出系统已经安装的内核版本 xff0c 在终端里输入命令 xff1a dpkg get selections grep linux image 然后会显示系统中已安装的内核 xff0c 例如 xff1a linux image 3 0 0
  • 你真的了解 bash 的 exec 命令吗?

    前言 bash exec 命令不太常用 xff0c 我对它提供的功能也仅知一二 xff0c 没有掌握全貌 最近遇到了几个问题 xff0c 从问题出发研究了一通找到 exec 命令头上 xff0c 问题迎刃而解后 xff0c 才对 exec