在Linux中用source,dot(.)和直接用脚本文件名执行shell脚本的区别

本文深入探讨了使用source和dot命令执行Shell脚本时的区别,特别是脚本内部变量在不同执行方式下的作用域。通过实验验证,展示了在当前shell中声明的变量在脚本执行前后是否可见,以及直接运行脚本文件如何导致产生子进程,进而影响变量作用域。

用source,dot(.)的方式执行shell脚本的时候,不产生子进程,shell脚本在当前的shell中运行,shell脚本运行完成后,在shell脚本中声明的变量在当前的shell中是可见的.

直接用脚本文件名的方式执行shell脚本的时候,产生子进程,shell脚本在子进程中运行,shell脚本运行完成后,在shell脚本中声明的变量在当前的shell中是不可见的.

验证过程:

在当前目录下有一个tt.sh的脚本内容如下:

echo $$
ttvar=12345

1,先来看当前的shell的pid:28210
test@btdalvm03:~/c$ echo $$
28210
2,以source的方式执行tt.sh,脚本打印的pid和当前shell的pid一致,在tt.sh中定义的变量ttvar在脚本执行完成后仍然可以访问.

test@btdalvm03:~/c$ source tt.sh
28210
test@btdalvm03:~/c$ echo $ttvar
12345

3,以dot方式执行和source效果一样,先用unset将ttvar变量清除.
test@btdalvm03:~/c$ unset ttvar
test@btdalvm03:~/c$ echo $ttvar

test@btdalvm03:~/c$ . tt.sh
28210
test@btdalvm03:~/c$ echo $ttvar
12345
4以脚本文件名称直接运行,要件当前文件夹加入PATH,(或者以./tt.sh指定文件名)

test@btdalvm03:~/c$ PATH=$PATH:.
test@btdalvm03:~/c$ unset ttvar
test@btdalvm03:~/c$ echo $ttvar

test@btdalvm03:~/c$ tt.sh
28796
test@btdalvm03:~/c$ echo $ttvar

test@btdalvm03:~/c$
可以看到这种方式,产生了新的子进程,脚本运行完成后,里面定义的变量对于当前的shell是不可访问的.
在改变sh的时候也是要产生子进程的,通过exit退回到改变之前的sh.
test@btdalvm03:~/c$ echo $$
28210
test@btdalvm03:~/c$ echo $$
28210
test@btdalvm03:~/c$ sh
sh-3.2$ echo $$
29152
sh-3.2$ bash
bash interactive changed
test@btdalvm03:~/c$ echo $$
29153
test@btdalvm03:~/c$ ps
  PID TTY          TIME CMD
28210 pts/1    00:00:00 bash
29152 pts/1    00:00:00 sh
29153 pts/1    00:00:00 bash
29205 pts/1    00:00:00 ps
test@btdalvm03:~/c$ exit
exit
sh-3.2$ echo $$
29152
sh-3.2$ exit
exit
test@btdalvm03:~/c$ echo $$
28210
test@btdalvm03:~/c$
<think>我们正在使用Qt在Linux环境下执行Shell脚本,包括启动关闭操作。根据引用[4],我们可以使用QProcess来执行Shell命令或脚本。下面将分别展示如何启动关闭Shell脚本。 ### 1. 执行Shell脚本执行一个Shell脚本,我们可以使用QProcess的`start`方法,并等待其完成。例如,假设我们有一个脚本`test.sh`,我们可以这样执行: ```cpp #include <QProcess> QString executeShellScript(const QString& scriptPath) { QProcess process; process.start("bash", QStringList() << scriptPath); // 使用bash执行脚本 process.waitForFinished(); // 等待执行完成 return process.readAllStandardOutput(); // 返回标准输出 } ``` ### 2. 启动后台运行的Shell脚本 如果脚本需要长时间运行,我们可能希望它在后台运行,这样不会阻塞我们的应用程序。我们可以使用`startDetached`方法,这样脚本会独立于当前进程运行,即使当前应用程序退出,脚本也可能继续运行(除非脚本依赖于应用程序的环境)。 ```cpp #include <QProcess> // 启动后台脚本,不等待完成 void startBackgroundScript(const QString& scriptPath) { QProcess::startDetached("bash", QStringList() << scriptPath); } ``` ### 3. 关闭正在运行的Shell脚本 关闭一个正在运行的脚本比较复杂,因为我们需要知道脚本的进程ID(PID)。有几种方法可以实现: - 在启动脚本时记录PID,然后使用`kill`命令结束它。 - 通过脚本名称查找并结束进程(这种方法可能有风险,因为可能有多个同名脚本运行)。 #### 方法1:启动时记录PID 我们可以通过启动脚本并获取其PID,然后存储起来,在需要时结束该进程。 ```cpp #include <QProcess> // 启动脚本并返回进程对象(以便后续操作) QProcess* startScriptAndGetProcess(const QString& scriptPath) { QProcess* process = new QProcess; process->start("bash", QStringList() << scriptPath); // 注意:这里不等待完成,所以需要管理process对象 return process; } // 关闭脚本(通过结束进程) void stopScript(QProcess* process) { if (process && process->state() == QProcess::Running) { process->terminate(); // 发送终止信号(SIGTERM) // 或者强制结束 // process->kill(); } delete process; // 清理 } ``` 但是,如果脚本产生了子进程,这种方法可能只结束父进程(即bash进程),而子进程可能会继续运行。为了避免这种情况,我们可以使用`pkill`命令来结束整个进程组。 #### 方法2:通过脚本名称结束 我们可以通过执行`kill`命令来结束所有运行该脚本的进程。但请注意,这可能会误杀同名的其他脚本。 ```cpp #include <QProcess> void stopScriptByName(const QString& scriptName) { QProcess process; // 使用pkill命令,根据脚本名称结束进程 // 注意:这里假设脚本名称是唯一的,且没有其他重要进程使用相同名称 process.start("pkill", QStringList() << "-f" << scriptName); process.waitForFinished(); } ``` ### 注意事项 1. 路径问题:确保提供的脚本路径是绝对路径或相对于当前工作目录的正确路径。 2. 权限问题:如果脚本需要执行权限,请确保已经使用`chmod +x script.sh`赋予了执行权限。 3. 环境变量:QProcess默认使用当前进程的环境变量。如果需要特定的环境,可以在启动前设置环境变量。 4. 错误处理:在实际应用中,需要添加错误处理,比如检查脚本是否存在,启动是否成功等。 ### 示例:完整的启动停止 假设我们有一个需要长时间运行的脚本`long_run.sh`,我们希望启动它并在需要时停止。 #### 启动脚本(后台运行,并记录PID) 我们可以修改脚本,使其在启动时写入自己的PID到一个文件,然后在停止时读取该文件来结束进程。但这里我们使用Qt来管理。 ```cpp class ScriptController : public QObject { Q_OBJECT public: explicit ScriptController(QObject *parent = nullptr) : QObject(parent) {} void startScript(const QString& scriptPath) { if (m_process) { qWarning() << "Script is already running!"; return; } m_process = new QProcess(this); connect(m_process, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), this, &ScriptController::onScriptFinished); m_process->start("bash", QStringList() << scriptPath); } void stopScript() { if (m_process && m_process->state() == QProcess::Running) { m_process->terminate(); // 发送终止信号 // 或者使用kill // m_process->kill(); } } private slots: void onScriptFinished(int exitCode, QProcess::ExitStatus exitStatus) { qDebug() << "Script finished with exit code:" << exitCode; m_process->deleteLater(); m_process = nullptr; } private: QProcess *m_process = nullptr; }; ``` 在这个例子中,我们使用一个QObject的子类来管理脚本进程。当调用`startScript`时,会启动脚本,并连接`finished`信号以便在脚本结束时清理。调用`stopScript`将终止正在运行的脚本进程。 ### 总结 在Qt中执行Shell脚本主要使用QProcess类。对于启动关闭,我们可以根据需求选择等待执行完成(同步)或后台运行(异步)。关闭脚本需要管理进程对象,并通过终止信号结束进程。 §§相关问题§§ 1. 在Qt中如何捕获Shell脚本的标准错误输出? 2. 如何确保在关闭应用程序时,所有由它启动的脚本进程都被正确终止? 3. 在Qt中执行Shell脚本时,如何传递参数?</think>### Qt C++ 在 Linux执行 Shell 脚本的实现 在 Linux 环境下,Qt 可以通过 `QProcess` 类高效地执行 Shell 脚本,实现启动关闭控制。以下是核心实现代码: #### 1. 基础执行函数(同步执行) ```cpp #include <QProcess> // 执行 Shell 命令/脚本并获取输出 QString executeShell(const QString& cmd) { QProcess process; process.start("bash", {"-c", cmd}); // 使用 bash 解释器 process.waitForFinished(); // 同步等待执行完成 return process.readAllStandardOutput(); } // 示例:执行脚本 void runScript() { QString output = executeShell("/path/to/your_script.sh"); qDebug() << "脚本输出:" << output; } ``` #### 2. 启动脚本(后台异步执行) ```cpp // 启动脚本(不阻塞主线程) void startScriptDetached(const QString& scriptPath) { QProcess::startDetached("bash", {"-c", scriptPath}); } // 示例:启动服务 startScriptDetached("/opt/myapp/start_service.sh"); ``` #### 3. 关闭脚本(通过 PID 终止) ```cpp // 终止正在运行的脚本 void stopScript(const QString& scriptName) { // 获取进程 PID QProcess pgrep; pgrep.start("pgrep", {"-f", scriptName}); pgrep.waitForFinished(); QString pid = pgrep.readAllStandardOutput().trimmed(); if (!pid.isEmpty()) { QProcess::execute("kill", {"-9", pid}); // 强制终止 } } // 示例:关闭服务 stopScript("my_background_script.sh"); ``` #### 4. 带参数执行脚本 ```cpp void runWithArgs() { QProcess process; QStringList args = {"arg1", "arg2", "arg3"}; process.start("/path/to/script.sh", args); process.waitForFinished(); } ``` ### 关键说明: 1. **同步 vs 异步** - `waitForFinished()`:阻塞主线程直到脚本完成 - `startDetached()`:后台运行不阻塞界面 2. **错误处理** ```cpp process.setProcessChannelMode(QProcess::MergedChannels); // 合并标准/错误输出 if (process.error() != QProcess::UnknownError) { qDebug() << "错误:" << process.errorString(); } ``` 3. **环境变量配置** ```cpp QProcess process; QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); env.insert("CUSTOM_VAR", "value"); process.setProcessEnvironment(env); ``` ### 应用场景示例 ```cpp // 启动监控脚本 startScriptDetached("/opt/monitor_system.sh"); // 执行配置更新(同步) executeShell("/opt/update_config.sh"); // 关闭服务 stopScript("data_processor.sh"); ``` > **注意**:需要确保目标脚本执行权限(`chmod +x script.sh`),且使用绝对路径避免路径错误。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值