前言
因为我是做的linux下开发,所以程序中需要多次在qt中调用linux命令行的命令,并且需要根据命令的执行结果做出相应的判断。qt中使用QProcess类实现进程间通信,也就是说QProcess可以调用外部程序并获取外部程序的信息。linux中通过启动bash(windows中启动cmd)来执行命令行的命令,并可以获取命令行的返回信息。
下面我详细介绍一下QProcess的具体用法和我踩过的坑以及注意事项:
QProcess的重要方法:
start(); //启动一个进程
close(); //关闭启动的外部进程
write(); //向外部进程写入数据
readAllStandardOutput(); //读取外部进程的标准输出
readAllStandardError(); //读取外部进程的错误信息
waitForStarted() //启动阻塞,等待程序启动完毕,期间整个程序所有进程阻塞
waitForFinished() //结束阻塞,等待程序结束完毕,期间整个程序所有进程阻塞
注意:QProcess中start()和write()中写的命令,末尾要加上\n(linux直接加\n,windows好像是\r\n,意思就是加个换行符),否则命令可能无法执行!!!
基础使用方法:
执行单条命令,且需要读取命令行返回信息时,代码如下:
QProcess pro;
connect(&pro,&QProcess::readyReadStandardOutput,this,[=,&pro]()mutable{ //注意我这里lambda表达式的写法,[]里的内容需要根据实际情况更改
QString Output=pro.readAllStandardOutput();
qDebug()<<"Output:"<<Output;
});
connect(&pro,&QProcess::readyReadStandardError,this,[=,&pro]()mutable{
QString Error=pro.readAllStandardError();
qDebug()<<"Error:"<<pro.readAllStandardError();
});
pro.start("bash"); //在start中写命令,可将括号里的内容直接换成命令
pro.waitForStarted(); //阻塞,等待bash启动完毕
pro.waitForFinished();
pro.close(); //在适当的位置关闭外部程序
优化
上面的代码是网上普遍给的使用方式,直接在start方法中写命令方便好用。但如果我想同时执行多条命令,且读出每次执行完命令的返回结果,这是一个QProcess对象就只能start一次。所以就必须把命令写在write()方法中,优化后的代码如下
QProcess pro;
connect(&pro,&QProcess::readyReadStandardOutput,this,[=,&pro]()mutable{ //注意我这里lambda表达式的写法,[]里的内容需要根据实际情况更改
QString Output=pro.readAllStandardOutput();
qDebug()<<"Output:"<<Output;
});
connect(&pro,&QProcess::readyReadStandardError,this,[=,&pro]()mutable{ //注意我这里lambda表达式的写法,[]里的内容需要根据实际情况更改
QString Error=pro.readAllStandardError();
qDebug()<<"Error:"<<pro.readAllStandardError();
});
pro.start("bash");
pro.waitForStarted(); //阻塞,等待bash启动完毕
pro.write("ls /root \n");
delayMSec(200); //非阻塞延迟给命令足够的执行时间,时间需根据命令的执行速度而定
QString path="/";
QString ls_command=QString("ls %1\n").arg(path); //执行带有变量的命令
pro.write(ls_command.toLocal8Bit().data());
delayMSec(200);
pro.close(); //在适当的位置关闭外部程序
非阻塞延迟delayMSec():–自己封装的
void MainWindow::delayMSec(unsigned int msec)
{
QTime Time_set = QTime::currentTime().addMSecs(msec);
while( QTime::currentTime() < Time_set )
QCoreApplication::processEvents(QEventLoop::AllEvents, 100);
}
注意:write()方法不可与waitForFinished()一起使用,否则会阻塞30秒。waitForFinished()只能用start()一起使用
什么是阻塞?很重要!!!
我查了很多的资料,网上的博客基本都是把阻塞一笔带过,但我在执行复杂的调用linux命令时,发现阻塞才是决定程序能否正确执行的关键。
我在阻塞上吃了很多亏,一遍一遍尝试代码,搜资料。
除了阻塞的地方是个坑外,qt中还无法识别管道“|”和重定向“>>”命令
解决方法:
QProcess pro;
pro.start("bash",QStringList() << "-c " << "ps -ef | grep firefox"); //执行带有管道的命令
pro.waitForStarted();
pro.waitForFinished();
qDebug()<<"pro.readAll:"<<pro.readAll();
pro.close(); //在适当的位置关闭外部程序
2022.8.8更新
pro.start("bash",QStringList() << "-c" << "ps -ef | grep firefox"); //执行带有管道的命令
注意:-c小写且前后没有空格,我之前的写法错了,导致一直无法运行,特此更新,注意注意!
原理是使用start的这个方法。意思是先打开一个程序,然后将字符数组的命令写到程序中,但不知道为什么要先加个-c,因为这里的命令是直接写到bash终端里的,所以末尾应该不用加\n
取代QProcess的方法-syetem()方法
system()是linux的方法,使用起来比QProcess更方便,但无法获取命令行的返回值。这里有个致命的问题就是有的命令有没有执行成功是必须要看命令行打印的信息的,system只能返回一个int数值,一般来说返回值为0说明该条命令执行完毕了,但无法确定有没有执行成功
system("ls");
system("systemctl start firewalld.service"); //这里可以写任意数量的命令
注意:system()也是阻塞命令,会等到命令执行完毕才会结束阻塞
但如果我们既需要执行多条命令,又要程序的返回信息,且又想让程序阻塞时间达到最小,可以将命令都写在shell脚本里,然后调用脚本,这样只需一次start,阻塞的问题也能解决
qt代码:
QProcess pro;
connect(&pro,&QProcess::readyReadStandardOutput,this,[=,&pro]()mutable{ //注意我这里lambda表达式的写法,[]里的内容需要根据实际情况更改
QString Output=pro.readAllStandardOutput();
qDebug()<<"Output:"<<Output;
});
connect(&pro,&QProcess::readyReadStandardError,this,[=,&pro]()mutable{
QString Error=pro.readAllStandardError();
qDebug()<<"Error:"<<pro.readAllStandardError();
});
QString dirpath= QCoreApplication::applicationDirPath(); //----获取可执行文件所在的目录
QString shell=QString(dirpath+"/test.sh %1 %2 %3\n").arg("/","/root","/home"); //解决传参问题
pro.start(shell); //执行脚本
pro.waitForStarted(); //阻塞,等待bash启动完毕
pro.waitForFinished();
pro.close();
脚本中代码:
#!/bin/sh
path1=$1
path2=$2
path3=$3
ls ${path1}
ls ${path2}
ls ${path3}
执行结果:
chmod 777 test.sh
注意:最后还是想说一下使用阻塞会导致界面产生卡顿。想要解决这个问题自然要使用多线程,将阻塞代码在其他线程中运行即可。
链接:qt中Qthread线程的使用以及安全关闭
码字不易,如果这篇博客对你有帮助,麻烦点赞收藏,非常感谢!有不对的地方