目录
原文链接:https://blog.csdn.net/YMGogre/article/details/128973098
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
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");
通过该方法执行的外部脚本输出会在 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 帮助文档或者官网的帮助文档都提供了该类十分详尽的介绍与使用方法,所以这里笔者就简单介绍了:
在 VxWorks、iOS、tvOS、watchOS 以及通用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() 方法:
start() 方法以父子进程的方式启动外部程序,父死子亡;
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) 提供了新数据时,就会发出此信号。不管当前的读通道是什么,它都会被触发;
原文链接:https://blog.csdn.net/YMGogre/article/details/128973098