在基于Qt的应用程序中启动外部Shell脚本的两种方法

目录

0、本文使用的环境配置:

1、新建一个简单的Shell脚本:

2、在Qt中启动外部Shell脚本:

2.1、使用标准库中提供的方法 —— system()

2.2、使用Qt提供的API —— QProcess类

2.2.1、使用start()方法启动外部Shell脚本

3、实例demo:

3.1、demo中的其他内容 —— QSettings


0、本文使用的环境配置:

  1. Ubuntu 20.04.5 LTS

  2. Qt 5.15.2 (GCC 7.3.1 20180303 (Red Hat 7.3.1-5), 64 bit)
  3. Qt Creator 5.0.0

1、新建一个简单的Shell脚本:

        Linux 的 Shell 种类众多,本文使用的是 Bourne Again Shell(/bin/bash),也就是被广泛使用的 Bash。这里我们先新建一个简单的 Bash 脚本给 Qt 程序启动用:

  • 首先前往一个你心仪的用于存放 Shell 脚本的文件夹(我选择在根目录下新建了一个名为“Bash_Script”的文件夹专门用于存放 Bash 脚本),在文件夹内打开终端,如下图所示:

  • 在终端内使用 touch 命令新建一个 *.sh 文件,随后使用 vi / vim / gedit 命令来编辑文件:
touch hello_world.sh

sudo gedit hello_world.sh
  • 在打开的文本编辑器中键入以下内容:
# 指定脚本解释器的目录
#!/bin/bash

while(true)
do
	echo "Hello World!"
	sleep 1s
done
  • 这是一个一秒打印一次 "Hello World!" 的 Shell 脚本。保存后关闭文本编辑器,回到终端,使用 chmod 命令来为脚本添加可执行权限:
chmod +x ./hello_world.sh
  • 然后键入以下命令就可以执行脚本了:
./hello_world.sh
  • 最后来张总览图:

2、在Qt中启动外部Shell脚本:

2.1、使用标准库中提供的方法 —— system()

system() 的原型为:

#include <stdlib.h>
int system(const char *__command)

        该方法调用 /bin/sh 来执行参数指定的命令,我们知道 /bin/sh 是用于指定脚本解释器的。GNU/Linux 操作系统中的 /bin/sh 本是 Bash (Bourne-Again Shell) 的符号链接,但鉴于 Bash 过于复杂,有人把 Bash NetBSD 移植到 Linux 并更名为 Dash (Debian Almquist Shell),并建议将 /bin/sh 指向它,以获得更快的脚本执行速度。Dash Shell 比 Bash Shell 小的多,符合POSIX标准。

        而 Ubuntu 继承了 Debian,所以从 Ubuntu 6.10 开始 /bin/sh 默认指向 Dash Shell:

        使用该方法十分简单,将想要执行的命令通过参数传递即可,比如我们可以使用绝对路径执行上一章新建的那个 Shell 脚本:

system("/home/xjy/Bash_Script/hello_world.sh");

        想要获取绝对路径我们可以在 Shell 脚本所在的文件夹内打开终端,输入 pwd(Print Working Directory) 命令即可获取当前文件夹的绝对路径

        通过该方法执行的外部脚本输出会在 Qt Creator 中通过应用程序输出栏打印出来:


        如果我们希望还是在终端中打印输出,那么我们可以用命令打开一个终端,再把执行脚本的命令传递给新打开的终端,就像下面这样:

system("gnome-terminal -- bash -c '/home/xjy/Bash_Script/hello_world.sh'&");

        这跟我们随意打开一个终端输入命令是一样的效果:

        不同的是现在我们可以在 Qt 中完成这一操作了。

        由以上两种操作可以看出,使用标准库的 system() 方法执行外部 Shell 脚本非常的简单,但我并没有研究如何在使用 system() 方法的情况下将 Shell 脚本的输出打印到我们的应用程序上。而且该方法还有个显而易见的缺点即当我们的 Shell 脚本是需要持续运行时,使用 system() 方法第一种操作(不开新的终端打印输出)去执行它会造成我们的主进程堵塞;而如果使用第二种操作(在终端里打印输出)虽然不会堵塞主进程了,但又会导致每次执行 Shell 都会打开一个终端,不够简练。

        如果你的程序对 Shell 脚本的输出打印位置没有要求或者 Shell 脚本可以迅速执行完毕的话,那就可以简单使用 system() 方法执行外部脚本即可。

2.2、使用Qt提供的API —— QProcess类

        当然,Qt 也提供给我们一个强大的 API 用于执行外部 Shell 脚本或者应用程序。QProcess 类是用于启动外部程序并与之传递信息的类。在 Qt Creator 帮助文档或者官网的帮助文档都提供了该类十分详尽的介绍与使用方法,所以这里笔者就简单介绍了:

  • VxWorksiOStvOSwatchOS 以及通用Windows平台 (Universal Windows Platform) 上不支持 QProcess 类;
  • 一个 QProcess 类的对象用于启动一个外部进程,如果我们想要同时执行多个持续运行的外部 Shell 脚本或应用程序,声明多个 QProcess 对象即可;
  • QProcess 提供了三个成员方法启动外部进程:
//静态成员方法execute,该方法会在一个新的进程中以arguments参数启动program程序,并等待其执行结束
int execute(const QString &program, const QStringList &arguments)

//public成员方法start,该方法会在一个新的进程中启动program程序,命令行参数则在arguments中传递
void QProcess::start(const QString &program, const QStringList &arguments, QIODevice::OpenMode mode = ReadWrite)

//静态成员方法startDetached,该方法以分离进程的方式在一个新的进程中以arguments参数启动program程序
bool startDetached(const QString &program, const QStringList &arguments, const QString &workingDirectory = QString(), qint64 *pid = nullptr)

        execute() 方法以堵塞进程的方式启动外部程序,这就和上一节的 system() 方法并无太大区别,这里就不再探讨了。既然选择使用 QProcess 类,那就重点关注 start()startDetached() 方法:

  1. start() 方法以父子进程的方式启动外部程序,父死子亡;
  2. startDetached() 方法以分离进程的方式启动外部程序,调用进程退出,分离进程不受影响继续运行;

        接下来笔者仅探讨使用 start() 方法启动外部 Shell 脚本,对 startDetached() 方法感兴趣的同学可以参考 Qt 官方文档

2.2.1、使用start()方法启动外部Shell脚本

  • 首先我们需要理解的是,Shell脚本是不需要编译的,而是直接运行解释器,将脚本作为解释器程序参数运行的。所以 start(const QString &program, const QStringList &arguments, QIODevice::OpenMode mode = ReadWrite) 方法第一个参数 “程序” 应为脚本解释器程序,而脚本文件(.sh)应当作为运行参数传入(也就是该方法的第二个参数)。所以,正确的调用姿势应当如下面这样:
#include <QProcess>

QProcess* executeProcess = new QProcess();
executeProcess->start("/bin/bash", QStringList() << "/home/xjy/Bash_Script/hello_world.sh");
  • 当然,我们也可以通过 setProgram(const QString &program) 方法设置进程要使用的程序;通过 setArguments(const QStringList &arguments) 方法设置在启动进程时传递给被调用程序的参数;最后通过调用 start(QIODevice::OpenMode mode = ReadWrite) 启动进程。但显然这样写更麻烦些,下面是一个例子:
#include <QProcess>

QProcess* executeProcess = new QProcess();
executeProcess->setProgram("/bin/bash");
executeProcess->setArguments(QStringList() << "/home/xjy/Bash_Script/hello_world.sh");
executeProcess->start();
  • 如果我们需要打印 Shell 脚本的输出,可以调用 readAllStandardOutput() 方法,不管当前的读通道是什么,该函数将进程标准输出的所有可用数据作为 QByteArray 返回。
QByteArray QProcess::readAllStandardOutput()
  • 此外,QProcess 类提供了一些信号是我们可以使用的:比如说当我们调用 start() 方法时,QProcess 对象会立即进入 Starting 状态;如果进程成功启动,QProcess 会发送 started() 信号,否则发送 errorOccurred(QProcess::ProcessError error) 信号。

        需要注意的是,进程是异步启动的,这意味着 started()errorOccurred() 信号可能会延迟。调用 waitForStarted() 以确保进程已经启动(或启动失败),并且已经发出了那些信号。

  • 还有一些信号比如 readyReadStandardOutput() 信号,当进程通过其标准输出通道 (stdout) 提供了新数据时,就会发出此信号。不管当前的读通道是什么,它都会被触发; 

3、实例demo:

        下面是一个在 Qt 应用程序中启动外部 Shell 脚本的实例,实例中包含2.2.1小节所有内容的可执行代码以及详尽注释。下载后可以直接用 Qt Creator 打开并编译运行:

基于Qt的应用程序启动外部Shell脚本demohttps://download.csdn.net/download/YMGogre/87544399

        当然您也可以选择把 shellselect 界面类单独拷贝出来include进您的项目中作为小部件窗口使用,具体步骤这里不再阐述。

使用效果如下图所示(本文提供的可下载demo版本要比演示GIF中的版本要高一些,界面有所更新,不过用法是一样的):

3.1、demo中的其他内容 —— QSettings

        demo中在应用程序里新增的脚本会作为应用程序设置保存下来,这个时候会使用到另外一个类 —— QSettings。它的效果如下图所示:

“测试”脚本并不会因为重启应用程序而消失

        QSettings 类提供持久的与平台无关的应用程序设置。 在 Qt Creator 帮助文档或者官网的帮助文档都提供了该类十分详尽的介绍与使用方法,所以这里笔者就简单介绍了:

  • 用户通常希望应用程序在会话期间记住它的设置(窗口大小和位置、选项等)。这些信息通常存储在Windows上的系统注册表中,在macOS和iOS上存储在属性列表文件中。在Unix系统上,由于缺乏标准,许多应用程序(包括KDE应用程序)使用INI文本文件;
  • QSettings 的 API 基于 QVariant,允许您以最简单的方式保存大多数基于值的类型,如QString, QRect 和 QImage
  • 创建 QSettings 对象时,您必须传递公司或组织的名称以及应用程序的名称。例如,如果您的产品叫Star Runner,您所在的公司叫MySoft,您将像下面这样构造 QSettings 对象:
    QSettings settings("MySoft", "Star Runner");
  • QSettings 对象可以在栈或者堆(new 出来的对象)中创建都可以;
  • 我们可以通过以下两个方法去设置或者获取值:
    //设置键key的值为value;如果key已经存在,则覆盖之前的值
    void QSettings::setValue(const QString &key, const QVariant &value)
    
    //返回键key的值;如果key不存在,返回defaultValue
    QVariant QSettings::value(const QString &key, const QVariant &defaultValue = QVariant()) const
  • 应用程序设置的文件存储位置可参考官方文档中Locations Where Application Settings Are Storedhttps://doc.qt.io/qt-6/qsettings.html#locations-where-application-settings-are-stored
  • demo中创建 QSettings 对象为 QSettings iniRead("Qt_Shell", "qt_shell_demo"); ,我们可以前往根目录,勾选“显示隐藏文件(H)”:

  • 找到名为“.config”的隐藏文件夹,其中就有一个名为“Qt_Shell”的文件夹内保存了名为“qt_shell_demo”的配置文件:

  • 13
    点赞
  • 60
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值