如何在Qt中执行复杂的Linux命令?

10
我想通过在Linux中运行QProcess命令来重新启动计算机。 我已经在我的应用程序中硬编码了根密码。
当我在终端中运行以下命令时,它完美地工作:
echo myPass | sudo -S shutdown -r now 

当我将命令放入一个 shell 脚本中,并通过 QProcess 调用它时,也是成功的:

QProcess process;
process.startDetached("/bin/sh", QStringList()<< "myScript.sh");

但是我无法直接通过传递给QProcess来运行它:

process.startDetached("echo myPass | sudo -S shutdown -r now ");

它只会打印出myPass | sudo -S shutdown -r now

如何直接使用QProcess运行这样相对复杂的命令。(而不是放在shell脚本中)

3个回答

14

为此目的而存在的关键方法在QProcess中建立:

void QProcess::setProcessChannelMode(ProcessChannelMode mode)

void QProcess::setStandardOutputProcess(QProcess * destination)

因此,以下代码片段将等同于command1 | command2,而不限制于一个解释器或另一个:

QProcess process1
QProcess process2;

process1.setStandardOutputProcess(&process2);

process1.start("echo myPass");
process2.start("sudo -S shutdown -r now");
process2.setProcessChannelMode(QProcess::ForwardedChannels);

// Wait for it to start
if(!process1.waitForStarted())
    return 0;

bool retval = false;
QByteArray buffer;
// To be fair: you only need to wait here for a bit with shutdown,
// but I will still leave the rest here for a generic solution
while ((retval = process2.waitForFinished()));
    buffer.append(process2.readAll());

if (!retval) {
    qDebug() << "Process 2 error:" << process2.errorString();
    return 1;
}

你可以省略 sudo -S 部分,因为你可以以 root 用户身份运行这个小程序并设置权限。你甚至可以为关机程序设置 setuid 或 setcap。

在构建商业 Linux 系统时,我们通常会有一个最小的应用程序,可以为其要执行的操作获得 setuid 或 setcap,并使用 system(3)QProcess 在 Linux 上显式地调用它。基本上,我会编写这个小应用程序,以避免将完整的 root 访问权限赋予整个应用程序,以此限制对恶意使用的访问权限,具体方法如下:

sudo chmod u+s /path/to/my/application

这个很好用,我喜欢它因为它更通用,更符合Qt的方式。我不知道这是干什么用的:process2.setProcessChannelMode(QProcess::ForwardedChannels); - Nejat
@Nejat:是的,没有那个也可能可以工作,但上次我没有那个就遇到了问题。它的目的是将子进程的输出重定向到主进程,所以,在你的情况下可能是不必要的。 - László Papp

3
首先,您可以配置sudo以避免要求输入密码。例如,成为sudo组的成员并拥有以下行:
 %sudo  ALL=NOPASSWD:  ALL

在您的/etc/sudoers文件中。当然,不要求密码会降低系统的安全性。
回答您关于Qt的问题,请记住bash(1),像所有Posix shell一样,因此/bin/sh接受带有字符串的-c参数(实际上system(3)正在fork一个/bin/sh -c)。所以只需执行
 process.startDetached("/bin/sh", QStringList()<< "-c" 
                       << "echo myPass | sudo -S shutdown -r now");

正如AntiClimacus回答的那样,在可执行文件中放置您的根密码是一个坏主意。

这是一个次优的想法,因为Qt有专门的API可以实现此功能,而不会限制您使用解释器。 - László Papp
此外,看起来 OP 已经尝试过这个,也问过: “如何直接使用 QProcess 运行相对复杂的命令(而不是放在 shell 脚本中)?” - László Papp

0
您需要将命令放入一个shell脚本中,并使用QProcess执行shbash并将您的shell脚本作为参数,因为您的命令包含|,必须由shbash解释。然而,这只是我的意见,但我不认为在可执行文件中包含根密码是一个好的解决方案。

不建议这样做,因为它会限制给定解释器的可用性。 - László Papp
我已经成功尝试过了,如果你读了问题的话。 - Nejat
@LaszloPapp:你知道有没有没有/bin/sh的Linux系统吗?这个问题是关于Linux的,不是所有操作系统。如果命令更复杂,有几个管道/重定向器,你会怎么做?10个QProcess?将命令专门放在一个shell脚本中也更灵活:如果你想改变命令,你不需要再次编译可执行文件。 - AntiClimacus
OP明确尝试并询问:“如何使用QProcess直接运行这些相对复杂的命令(而不是放在shell脚本中)?”->您没有尝试回答问题。此外,您也感到困惑:解释器不是sh,而是bash。是的,整个嵌入式行业都基于busybox或toybox,没有bash。许多人在桌面上也使用zsh而不是bash。 OP想要避免它并找到一个跨平台的解决方案。我仍然认为您只是重申OP写的内容,因此这不是回答问题的尝试。 - László Papp
我并不困惑:sh 不是 bash,也不总是符号链接到 bash,sh 是一个完整且独立的 shell(http://en.wikipedia.org/wiki/Bourne_shell)。即使没有 bash 和 sh,例如如果您使用 zsh、dash 或其他 shell,则在这种情况下,sh 指向正确的解释器。 - AntiClimacus
你似乎仍然很困惑。你没有回答问题的提问者。你写的是他已经尝试过并希望避免的内容。就 shell 而言,不同的 shell 可能有不同的语法来处理管道等。这方案将是特定的,不一定适用于另一个 shell。我不确定其中什么不清楚,或者它与符号链接指向 bash、busybox 的 ash 等有什么关系。在我看来,那似乎是个红鲱鱼。 - László Papp

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接