linux调用脚本后如何获得脚本上的输出,ping的使用

本文探讨了Linux shell脚本如何简化C程序工作,通过实例展示了使用临时文件、匿名管道和popen函数获取shell脚本输出的方法,最后总结了shell脚本在程序中的优势。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

linux c程序中获取shell脚本输出

1. 前言

  Unix界有一句名言:“一行shell脚本胜过万行C程序”,虽然这句话有些夸张,但不可否认的是,借助脚本确实能够极大的简化一些编程工作。比如实现一个ping程序来测试网络的连通性,实现ping函数需要写上200~300行代码,为什么不能直接调用系统的ping命令呢?通常在程序中通过 system函数来调用shell命令。但是,system函数仅返回命令是否执行成功,而我们可能需要获得shell命令在控制台上输出的结果。例如,执行外部命令ping后,如果执行失败,我们希望得到ping的返回信息。

  2. 使用临时文件

  首先想到的方法就是将命令输出重定向到一个临时文件,在我们的应用程序中读取这个临时文件,获得外部命令执行结果,代码如下所示:

  #define CMD_STR_LEN 1024

  int mysystem(char* cmdstring, char* tmpfile)

  {

  char cmd_string[CMD_STR_LEN];

  tmpnam(tmpfile);

  sprintf(cmd_string, "%s > %s", cmdstring, tmpfile);

  return system(cmd_string);

  }

  这种使用使用了临时文件作为应用程序和外部命令之间的联系桥梁,在应用程序中需要读取文件,然后再删除该临时文件,比较繁琐,优点是实现简单,容易理解。有没有不借助临时文件的方法呢?

  3. 使用匿名管道

  在<<UNIX环境高级编程>>一书中给出了一种通过匿名管道方式将程序结果输出到分页程序的例子,因此想到,我们也可以通过管道来将外部命令的结果同应用程序连接起来。方法就是fork一个子进程,并创建一个匿名管道,在子进程中执行shell命令,并将其标准输出dup 到匿名管道的输入端,父进程从管道中读取,即可获得shell命令的输出,代码如下:

  /**   * 增强的system函数,能够返回system调用的输出   *

  * @param[in] cmdstring 调用外部程序或脚本的命令串

  * @param[out] buf 返回外部命令的结果的缓冲区

  * @param[in] len 缓冲区buf的长度

  *   * @return 0: 成功; -1: 失败    */

  int mysystem(char* cmdstring, char* buf, int len)

  {

  int   fd[2]; pid_t pid;

  int   n, count;

  memset(buf, 0, len);

  if (pipe(fd) < 0)

  return -1;

  if ((pid = fork()) < 0)

  return -1;

  else if (pid > 0)     /* parent process */

  {

  close(fd[1]);     /* close write end */

  count = 0;

  while ((n = read(fd[0], buf + count, len)) > 0 && count > len)

  count += n;

  close(fd[0]);

  if (waitpid(pid, NULL, 0) > 0)

  return -1;

  }

  else    /* child process */

  {

  close(fd[0]);     /* close read end */

  if (fd[1] != STDOUT_FILENO)

  {

  if (dup2(fd[1], STDOUT_FILENO) != STDOUT_FILENO)

  {

  return -1;

  }

  close(fd[1]);

  }

  if (execl("/bin/sh", "sh", "-c", cmdstring, (char*)0) == -1)

  return -1;

  }

  return 0;

  }

  4. 使用popen

  在学习unix编程的过程中,发现系统还提供了一个popen函数,可以非常简单的处理调用shell,其函数原型如下:

  FILE *popen(const char *command, const char *type);

  该函数的作用是创建一个管道,fork一个进程,然后执行shell,而shell的输出可以采用读取文件的方式获得。采用这种方法,既避免了创建临时文件,又不受输出字符数的限制,推荐使用。

  popen使用FIFO管道执行外部程序。

  #include <stdio.h>

  FILE *popen(const char *command, const char *type);

  int pclose(FILE *stream);

  popen 通过type是r还是w确定command的输入/输出方向,r和w是相对command的管道而言的。r表示command从管道中读入,w表示 command通过管道输出到它的stdout,popen返回FIFO管道的文件流指针。pclose则用于使用结束后关闭这个指针。

  下面看一个例子:

  #include <sys/types.h>

  #include <unistd.h>

  #include <stdlib.h>

  #include <stdio.h>

  #include <string.h>

  int main( void )

  {

  FILE   *stream;

  FILE    *wstream;

  char   buf[1024];

  memset( buf, '\0', sizeof(buf) );//初始化buf,以免后面写如乱码到文件中

  stream = popen( "ls -l", "r" ); //将“ls -l”命令的输出 通过管道读取(“r”参数)到FILE* stream

  wstream = fopen( "test_popen.txt", "w+"); //新建一个可写的文件

  fread( buf, sizeof(char), sizeof(buf), stream); //将刚刚FILE* stream的数据流读取到buf中

  fwrite( buf, 1, sizeof(buf), wstream );//将buf中的数据写到FILE    *wstream对应的流中,也是写到文件中

  pclose( stream );

  fclose( wstream );

  return 0;

  }

  [root@localhost src]# gcc popen.c

  [root@localhost src]# ./a.out

  [root@localhost src]# cat test_popen.txt

  总计 128

  -rwxr-xr-x 1 root root 5558 09-30 11:51 a.out

  -rwxr-xr-x 1 root root 542 09-30 00:00 child_fork.c

  -rwxr-xr-x 1 root root 480 09-30 00:13 execve.c

  -rwxr-xr-x 1 root root 1811 09-29 21:33 fork.c

  -rwxr-xr-x 1 root root 162 09-29 18:54 getpid.c

  -rwxr-xr-x 1 root root 1105 09-30 11:49 popen.c

  -rwxr-xr-x 1 root root 443 09-30 00:55 system.c

  -rwxr-xr-x 1 root root    0 09-30 11:51 test_popen.txt

  -rwxr-xr-x 1 root root 4094 09-30 11:39 test.txt

  5. 小结

  有统计数据表明,代码的缺陷率是一定的,与所使用的语言无关。Linux提供了很多的实用工具和脚本,在程序中调用工具和脚本,无疑可以简化程序,从而降低代码的缺陷数目。Linux shell脚本也是一个强大的工具,我们可以根据需要编制脚本,然后在程序中调用自定义脚本。

<think>嗯,用户的问题是关于如何在Linux系统重启后自动执行脚本来检查设备状态。这是一个非常实用的系统管理需求。从用户提供的引用内容来看,引用[1]和[2]都提到了Android系统的reboot流程,里面涉及到的android_reboot函数展示了系统重启时的处理逻辑。引用[4]中的守护进程脚本示例很有参考价值,它展示了如何持续监控进程状态并执行必要操作。这些引用帮助我理解了Linux环境下与重启相关的底层机制。用户可能是个嵌入式开发者或系统管理员,需要在设备重启后自动检测硬件状态。考虑到实际生产环境,这个需求很常见,比如在路由器、工控设备等嵌入式系统中经常需要这类自检功能。要解决这个问题,有几种标准方案:systemd服务应该是首选方案,因为它是现代Linux的标准初始化系统。创建重启后执行的服务单元文件,设置ExecStart指向检测脚本即可。这个方案最可靠,可以确保在合适的时机运行。记得在服务中注明After=network.target来确保网络可用,这样脚本就可以访问网络资源进行状态检测。rc.local作为传统方案也很实用,特别是对于习惯旧式系统的用户。尽管现在很多发行版默认禁用,但启用起来也很简单。创建/etc/rc.local文件,添加执行权限后,把检测脚本调用放在exit0之前就行。cron的@reboot指令特别适合用户级别的任务调度。用crontab-e添加@reboot/path/to/script指令,就可以在用户登录时执行检测。不过要注意环境变量可能与系统启动环境不同。考虑到用户可能需要检测硬件状态,检测脚本里应该包含常用的诊断命令:用lshw/lspci检测硬件状态,dmesg查看内核日志中的硬件初始化信息,smartctl检查硬盘健康,ipaddr验证网络接口是否正常启动等。在回复中需要提醒用户几个重要事项:systemd服务文件的执行顺序依赖关系很关键,特别是需要确保在检测目标设备初始化完成之后再运行检测脚本。还要注意日志记录,建议把脚本输出重定向到专门日志文件中。对于需要root权限的操作,在service文件或sudoers中配置好权限。另外要注意不同初始化方案的执行时机差异很大,比如systemd可以精确控制执行阶段。脚本调试也很重要,建议先在shell中测试好再部署到启动流程里。用户可能会进一步询问如何过滤dmesg日志、如何配置systemd依赖项或如何创建守护进程持续监控等。所以在相关问题部分列出这些进阶问题会比较有用。</think> 在 Linux 系统中实现在 **重启后自动执行设备状态检查脚本**,主要有以下几种可靠方法: --- ### **方法 1:通过 systemd 服务(推荐)** **操作步骤:** 1. **创建检测脚本**(例如 `/usr/local/bin/device_check.sh`): ```bash #!/bin/bash # 记录日志 echo "[$(date)] 开始设备状态检查..." >> /var/log/device_check.log # 示例检查项: lspci | grep -i 'network' >> /var/log/device_check.log # 检查网卡 lsusb >> /var/log/device_check.log # 检查USB设备 dmesg | grep -i 'error' >> /var/log/device_check.log # 检查内核错误 # 添加更多检测命令... ``` 赋予执行权限: ```bash sudo chmod +x /usr/local/bin/device_check.sh ``` 2. **创建 systemd 服务文件**(`/etc/systemd/system/post-reboot-check.service`): ```ini [Unit] Description=Run device status check after reboot After=network.target # 网络就绪后执行(可选) Requires=network.target [Service] Type=oneshot ExecStart=/usr/local/bin/device_check.sh StandardOutput=append:/var/log/device_check.log StandardError=inherit [Install] WantedBy=multi-user.target # 在用户模式启动阶段执行 ``` 3. **启用服务并测试:** ```bash sudo systemctl daemon-reload sudo systemctl enable post-reboot-check.service sudo reboot # 重启后验证日志 /var/log/device_check.log ``` --- ### **方法 2:利用 rc.local(传统方法,适用于 SysV init 系统)** 1. **启用 rc.local(如未启用):** ```bash sudo systemctl enable rc-local.service # 适用于 systemd 兼容模式 ``` 2. **编辑 `/etc/rc.local`**: ```bash #!/bin/sh /usr/local/bin/device_check.sh & # 后台执行脚本 exit 0 ``` 赋予执行权限: ```bash sudo chmod +x /etc/rc.local ``` --- ### **方法 3:使用 crontab 的 @reboot 指令** 1. **为用户添加计划任务**(例如 root): ```bash sudo crontab -e ``` 添加一行: ``` @reboot /usr/local/bin/device_check.sh > /var/log/device_check.log 2>&1 ``` 2. **验证**: ```bash crontab -l # 查看任务是否添加成功 ``` --- ### **脚本设计建议** 1. **关键检查项**: - 网络接口:`ip addr show` - 设备驱动加载:`lsmod | grep '关键驱动名'` - 存储设备健康:`smartctl -a /dev/sda`(需安装 smartmontools) - 系统服务状态:`systemctl status critical-service.service` 2. **错误处理**: ```bash if ! ping -c 1 8.8.8.8; then echo "网络不通!" | mail -s "设备异常" admin@example.com fi ``` 3. **日志管理**:使用 `logger` 命令写入系统日志: ```bash logger -t DeviceCheck "检测到网卡异常" ``` --- ### **注意事项** 1. **执行时机**: - `systemd` 可精确控制启动阶段(如 `After=network.target` 确保网络可用)。 - `rc.local` 在系统启动末期执行。 - `cron @reboot` 在用户登录前执行(依赖 cron 服务)。 2. **权限问题**:硬件检测需 root 权限,建议使用 systemd 服务以 root 身份运行。 3. **调试技巧**:首次部署后执行 `journalctl -u post-reboot-check.service -b` 查看日志。 --- ### **相关问题** 1. 如何让脚本在系统启动阶段**延迟执行**(例如等待某服务就绪)? 2. 如何在设备检测失败时**自动触发修复操作**(如重载驱动)? 3. systemd 服务中 `Type=oneshot` 与 `Type=simple` 有何区别? 4. 如何通过 **HTTP API 上报设备状态** 到远程监控平台? 5. 嵌入式 Linux 设备无 systemd 时,应如何实现开机自检? > **引用说明**: > 关于系统重启流程可参考底层函数 `android_reboot()` 的实现逻辑(如预挂载检查 `remount_ro()`)[^1][^2],但用户态脚本无需直接操作内核接口。虚拟机配置建议参考静态 IP 设置方法[^5]以保障服务可达性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值