Linux C 中执行shell命令

文章目录

    • 摘要
    • 前言
    • exec函数的使用
    • system函数使用
    • popen函数的使用

摘要

本文尝试使用exec,system,popen函数,来执行一个shell命令。(1) 如果只需要执行命令后的返回值,不关心标准输出,错误输出,可以使用system函数。(2) 如果希望拿到返回值,标准输出,可以使用popen。(2) 如果前面两个函数都不能满足要求,那使用exec,虽然这个比较麻烦。


前言

老实说(To be honest), 在Linux c 中,调用exec/system/popen来执行shell命令,都不太完美。exec的缺点是用起来比较麻烦。system和popen是封装的还不够好。

谈论好/坏之前,需要建立评价标准。或者说,需要实现哪些功能,才算好了。我们参考下Boost.Process,一个C++进程库有哪些功能。

  • 创建一个子进程。
  • 为子进程设置输入/输出流(为子进程设置输入流,读取子进程的标准输出/错误输出)(同步和异步)。
  • 等待进程结束,获取返回码(同步和异步)。
  • 终止子进程。

system函数,只能拿到返回值; popen只能设置标准输入,或者标准输出,没法单独获取到错误输出。而想要使用exec函数,优雅又安全的实现上面功能,不容易,挺不容易。

哎呀,凑活着用就是嘞,像生活一样。


exec函数的使用

参考: exec(3) - Linux manual page , 《unix环境高级编程》8.10 函数exec

这个函数不太好写,我也不咋喜欢用,因为有些麻烦。我们看下面这个示例。

#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <syslog.h>
#include <unistd.h>int main(int argc, char *argv[]) {openlog("exec", LOG_PERROR, 0);char *cmd = "ls";char *ls_argv[] = {"ls", "-alh", "NO_EXIST_FILE", NULL};pid_t pid;if ((pid = fork()) < 0) {syslog(LOG_ERR, "fork error");} else if (pid == 0) { /* specify pathname, specify environment */if (execvp(cmd, ls_argv) < 0) {syslog(LOG_ERR, "execvp error");}}int status;if (waitpid(pid, &status, 0) < 0) {syslog(LOG_ERR, "wait error");} else {if (WIFEXITED(status)) {int ret = WEXITSTATUS(status);syslog(LOG_INFO, "subprocess return code: %d", ret);}}exit(0);
}

这个程序很简单,创建一个子进程并执行shell命令。父进程等待子进程结束。

仔细推敲的话,上面的实现是有问题的。

(1) fork() 执行失败的时候,会返回-1。而waitpid(3) - Linux man page的第一个参数是-1时,表示等待任意一个子进程,而不是我们目前希望的子进程。如果此时没有自进程,它会立即出错返回。
(2) 上面,我们希望拿到子进程执行后的返回码。但是如果程序不是正常(return,exit方式)结束,比如信号终止或者coredump,是拿不到子进程本身的返回值的。


system函数使用

参考:system(3) - Linux manual page , 《unix环境高级编程》8.13 函数system

system函数的行为,像是使用fork创建子进程,然后像下面这个调用exec函数。

execl("/bin/sh", "sh", "-c", command, (char *) NULL);

父进程在执行命令的期间,会阻塞SIGCHLD信号,忽略SIGINT和SIGQUIT信号。

我挺喜欢system函数。因为它的接口简单,易上手。下面,我们使用system函数,重写下上面的程序。

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <unistd.h>int main(int argc, char *argv[]) {openlog("system", LOG_PERROR, 0);char *cmd = "ls -alh NO_EXIST_FILE";int status = system(cmd);if (status == -1) {syslog(LOG_ERR, "create subprocess failed: %s", strerror(errno));goto err;}if (WIFEXITED(status)) {int ret = WEXITSTATUS(status);syslog(LOG_INFO, "subprocess return code: %d", ret);}err:exit(0);
}

可惜的是,拿不到标准错误输出。这个错误输出直接输出到命令行了,不方便写入日志。

输出如下。

ls: cannot access 'NO_EXIST_FILE': No such file or directory
system: subprocess return code: 2

popen函数的使用

程序员这个行业,某些时候真是太无聊了,必须得花时间在“茴香豆的茴有哪些写法”上。

我们再看看看popen的使用。

参考: popen(3) - Linux manual page , 《unix环境高级编程》15.3 函数popen和pclose

我们继续使用popen实现上面代码的功能。(红烧鱼,清蒸鱼,糖醋鲤鱼,一🐟多吃)

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <syslog.h>
#include <unistd.h>int main(int argc, char *argv[]) {openlog("system", LOG_PERROR, 0);char *cmd = "ls -alh NO_EXIST_FILE 2>&1";//   char *cmd = "ls -alh NO_EXIST_FILE";FILE *fp = popen(cmd, "r");if (fp == NULL) {syslog(LOG_ERR, "create subprocess failed: %s", strerror(errno));exit(0);}char line[1024];while (fgets(line, sizeof(line), fp) != NULL) {printf("header: %s", line);}int status = pclose(fp);if (status == -1) {syslog(LOG_ERR, "pclose failed: %s", strerror(errno));} else if (WIFEXITED(status)) {int ret = WEXITSTATUS(status);syslog(LOG_INFO, "subprocess return code: %d", ret);}exit(0);
}

关于popen接口的使用,自行参考官方文档。下面看两个更有意思的问题。

问题一:能否单独获取标准错误输出,而不是将标准错误输出混在标准输出中。

答案是可以,但不是一个好主意,得引入select这样的函数,来保证可以同时读取两个流。可以参考: c popen won’t catch stderr - Stack Overflow

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/736572.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于java+springboot+vue实现的火车票订票系统(文末源码+Lw)294

摘要 火车票订票系统可以对火车票订票系统信息进行集中管理&#xff0c;可以真正避免传统管理的缺陷。火车票订票系统是一款运用软件开发技术设计实现的应用系统&#xff0c;在信息处理上可以达到快速的目的&#xff0c;不管是针对数据添加&#xff0c;数据维护和统计&#xf…

HAproxy反向代理与负载均衡

目录 一、HAproxy介绍 1. 概述 2. 关于4/7层负载均衡 2.1 无负载均衡 2.1.1 图示 2.1.2 说明 2.2 四层负载均衡 2.2.1 图示 2.2.2 说明 2.3 七层负载 2.3.1 图示 2.3.2 说明 3. 特性 4. HAProxy负载均衡常见策略 5. 处理模式 二、HAproxy安装 1. yum安装 2. 第…

Vue - v-if和v-else-if和v-else的使用

一、简单的演示 <body><div id"app"><p v-if"score>90">优秀</p><p v-else-if"score>60">及格</p></div><script src"../js/vue.js"></script><script>const app…

3月11日代码随想录电话号码的字母组合

17.电话号码的字母组合 给定一个仅包含数字 2-9 的字符串&#xff0c;返回所有它能表示的字母组合。答案可以按 任意顺序 返回。 给出数字到字母的映射如下&#xff08;与电话按键相同&#xff09;。注意 1 不对应任何字母。 示例 1&#xff1a; 输入&#xff1a;digits &q…

mysql的索引、事务、分库分表问题

1.了解MySQL的索引吗&#xff1f;它为什么使用Btree作为底层&#xff0c;而不是其他呢&#xff1f; 这里我们要谈的是其他数据结构的缺点&#xff0c;然后说说Btree的优点&#xff0c;也就看你对MySQL的Btree与其他数据结构熟不熟悉。 Hash &#xff08;1&#xff09;Hash 索引…

Error running ‘Attach debug to process‘

这里写自定义目录标题 Ubuntu导入源码调试遇到错误 Ubuntu导入源码调试遇到错误 打开调试UI&#xff0c;选择system_process进程&#xff0c;直接右下角弹出错误对话框。错误如下&#xff1a; Error running ‘Attach debug to process’ Unable to find project context to …

[HackMyVM]靶场 Espo

kali:192.168.56.104 主机发现 arp-scan -l # arp-scan -l Interface: eth0, type: EN10MB, MAC: 00:0c:29:d2:e0:49, IPv4: 192.168.56.104 Starting arp-scan 1.10.0 with 256 hosts (https://github.com/royhills/arp-scan) 192.168.56.1 0a:00:27:00:00:05 (Un…

openAI key 与ChatGPTPlus的关系,如何升级ChatGPTPLus

一、前言 先详细介绍一下Plus会员和Open API之间的区别&#xff1a; 实际上&#xff0c;这两者是相互独立的。举例来说&#xff0c;虽然您开通了Plus会员&#xff0c;并不意味着您就可以使用4.0版本的API。尽管这两个账户可以是同一个&#xff0c;但它们是完全独立的平台。 …

小程序设备控制API能力汇总——DP相关API

ty.device.publishDps 下发 DP 功能点 需引入DeviceKit&#xff0c;且在>1.2.6版本才可使用 请求参数 Object object 属性类型默认值必填说明deviceIdstring是设备 iddpsRecord<dpId, any>是dpsmodenumber是下发通道类型 0: 局域网 1: 网络 2: 自动pipelinesArray&…

rocketmq学习笔记(一)安装部署

初次使用rocketmq&#xff0c;记录一下全流程步骤。 1、下载安装包 首先在官网&#xff0c;下载安装包&#xff0c;可也根据官方文档进行部署&#xff0c;但有一些细节没说明&#xff0c;可能会有坑&#xff0c;本文会尽量详细的描述每个步骤&#xff0c;把我踩过的坑填补上。…

后端八股笔记------Redis

Redis八股 上两种都有可能导致脏数据 所以使用两次删除缓存的技术&#xff0c;延时是因为数据库有主从问题需要更新&#xff0c;无法达到完全的强一致性&#xff0c;只能达到控制一致性。 一般放入缓存中的数据都是读多写少的数据 业务逻辑代码&#x1f447; 写锁&#x1f4…

[BT]小迪安全2023学习笔记(第20天:Web攻防-PHP特性)

第20天 和 &#xff08;等值比较&#xff09; 当使用 操作符时&#xff0c;PHP将进行宽松比较&#xff0c;也就是说&#xff0c;只比较两个值的等价性&#xff0c;而不考虑它们的类型。 如果两个值类型不同&#xff0c;PHP会尝试将它们转换成相同的类型&#xff0c;然后再进…

基础 | JVM - [指令 性能监控]

INDEX jps&#xff08;jvm 进程工具&#xff09;jinfo&#xff08;java 配置信息工具&#xff09;jstack &#xff08;查看虚拟机栈信息&#xff09;jmap&#xff08;jvm 内存影像工具&#xff09;jstat&#xff08;jvm 统计信息监控工具&#xff09;jvisualvm&#xff08;查看…

【NR 定位】3GPP NR Positioning 5G定位标准解读(十)-增强的小区ID定位

前言 3GPP NR Positioning 5G定位标准&#xff1a;3GPP TS 38.305 V18 3GPP 标准网址&#xff1a;Directory Listing /ftp/ 【NR 定位】3GPP NR Positioning 5G定位标准解读&#xff08;一&#xff09;-CSDN博客 【NR 定位】3GPP NR Positioning 5G定位标准解读&#xff08;…

对比才有伤害!ChatGPT 4.0 VS Claude 3,这就是ChatGPT偷懒变慢的根本原因!附解决方案

大家好&#xff0c;我是木易&#xff0c;一个持续关注AI领域的互联网技术产品经理&#xff0c;国内Top2本科&#xff0c;美国Top10 CS研究生&#xff0c;MBA。我坚信AI是普通人变强的“外挂”&#xff0c;所以创建了“AI信息Gap”这个公众号&#xff0c;专注于分享AI全维度知识…

算法D38 | 动态规划1 | 509. 斐波那契数 70. 爬楼梯 746. 使用最小花费爬楼梯

理论基础 无论大家之前对动态规划学到什么程度&#xff0c;一定要先看 我讲的 动态规划理论基础。 如果没做过动态规划的题目&#xff0c;看我讲的理论基础&#xff0c;会有感觉 是不是简单题想复杂了&#xff1f; 其实并没有&#xff0c;我讲的理论基础内容&#xff0c;在动…

每日一练:LeeCode-56、合并区间【数组+滑动窗口】

4.合并区间 LeeCode-56、合并区间 以数组 intervals 表示若干个区间的集合&#xff0c;其中单个区间为 intervals[i] [starti, endi] 。请你合并所有重叠的区间&#xff0c;并返回 一个不重叠的区间数组&#xff0c;该数组需恰好覆盖输入中的所有区间 。 1 < intervals.le…

GitLab 的具体步骤

安装操作系统&#xff1a;选择适合的操作系统&#xff0c;如 Linux、Windows 等。配置服务器&#xff1a;安装必要的软件和服务&#xff0c;如 Web 服务器、数据库等。安装 GitLab&#xff1a;使用官方安装包或自行编译安装&#xff0c;根据提示进行安装。配置数据库&#xff1…

Math类 --Java学习笔记

Math 代表数学&#xff0c;是一个工具类&#xff0c;里面提供的都是对数据进行操作的一些静态方法 Math提供的常用方法

C语言分析基础排序算法——交换排序

目录 交换排序 冒泡排序 快速排序 Hoare版本快速排序 挖坑法快速排序 前后指针法快速排序 快速排序优化 快速排序非递归版 交换排序 冒泡排序 见C语言基础知识指针部分博客C语言指针-CSDN博客 快速排序 Hoare版本快速排序 Hoare版本快速排序的过程类似于二叉树前序…