在 Shell 脚本中调用另一个 Shell 脚本的三种方式

部署运行你感兴趣的模型镜像

先来说一下主要以下有几种方式:

  • fork: 如果脚本有执行权限的话,path/to/foo.sh。如果没有,sh path/to/foo.sh
  • exec: exec path/to/foo.sh
  • source: source path/to/foo.sh

fork

fork 是最普通的, 就是直接在脚本里面用 path/to/foo.sh 来调用
foo.sh 这个脚本,比如如果是 foo.sh 在当前目录下,就是 ./foo.sh。运行的时候 terminal 会新开一个子 Shell 执行脚本 foo.sh,子 Shell 执行的时候, 父 Shell 还在。子 Shell 执行完毕后返回父 Shell。 子 Shell 从父 Shell 继承环境变量,但是子 Shell 中的环境变量不会带回父 Shell。

exec

execfork 不同,不需要新开一个子 Shell 来执行被调用的脚本. 被调用的脚本与父脚本在同一个 Shell 内执行。但是使用 exec 调用一个新脚本以后, 父脚本中 exec 行之后的内容就不会再执行了。这是 execsource 的区别.

source

fork 的区别是不新开一个子 Shell 来执行被调用的脚本,而是在同一个 Shell 中执行. 所以被调用的脚本中声明的变量和环境变量, 都可以在主脚本中进行获取和使用。

其实从命名上可以感知到其中的细微区别,下面通过两个脚本来体会三种调用方式的不同:

第一个脚本,我们命名为 1.sh:

#!/usr/bin/env bash

A=1

echo "before exec/source/fork: PID for 1.sh = $$"

export A
echo "In 1.sh: variable A=$A"

case $1 in
        --exec)
                echo -e "==> using exec…\n"
                exec ./2.sh ;;
        --source)
                echo -e "==> using source…\n"
                . ./2.sh ;;
        *)
                echo -e "==> using fork by default…\n"
                ./2.sh ;;
esac

echo "after exec/source/fork: PID for 1.sh = $$"
echo -e "In 1.sh: variable A=$A\n"

第二个脚本,我们命名为 2.sh

#!/usr/bin/env bash

echo "PID for 2.sh = $$"
echo "In 2.sh get variable A=$A from 1.sh"

A=2
export A

echo -e "In 2.sh: variable A=$A\n"

注:这两个脚本中的参数 $$ 用于返回脚本的 PID , 也就是进程 ID。这个例子是想通过显示 PID 判断两个脚本是分开执行还是同一进程里执行,也就是是否有新开子 Shell。当执行完脚本 2.sh 后,脚本 1.sh 后面的内容是否还执行。

chmod +x 1.sh 2.sh 给两个脚本加上可执行权限后执行情况:

fork

fork

fork 方式可以看出,两个脚本都执行了,运行顺序为1-2-1,从两者的PID值(1.sh PID=82266, 2.sh PID=82267),可以看出,两个脚本是分成两个进程运行的。

exec

exec

exec 方式运行的结果是,2.sh 执行完成后,不再回到 1.sh。运行顺序为 1-2。从pid值看,两者是在同一进程 PID=82287 中运行的。

source

source

source方式的结果是两者在同一进程里运行。该方式相当于把两个脚本先合并再运行。

CommandExplanation
fork新开一个子 Shell 执行,子 Shell 可以从父 Shell 继承环境变量,但是子 Shell 中的环境变量不会带回给父 Shell。
exec在同一个 Shell 内执行,但是父脚本中 exec 行之后的内容就不会再执行了
source在同一个 Shell 中执行,在被调用的脚本中声明的变量和环境变量, 都可以在主脚本中进行获取和使用,相当于合并两个脚本在执行。

参考:
- 在shell脚本中调用另一个脚本的三种不同方法(fork, exec, source)

您可能感兴趣的与本文相关的镜像

Qwen3-8B

Qwen3-8B

文本生成
Qwen3

Qwen3 是 Qwen 系列中的最新一代大型语言模型,提供了一整套密集型和专家混合(MoE)模型。基于广泛的训练,Qwen3 在推理、指令执行、代理能力和多语言支持方面取得了突破性进展

<think>嗯,用户这次问的是如何在Shell脚本调用其他目录下的脚本,正好之前提供的引用材料有多处相关内容可以整合。用户似乎是个Shell脚本初学者,从提问方式看可能正在写跨目录调用脚本。需要特别注意用户可能遇到的实际问题,比如权限或路径错误——这点在引用[4]提到的sh和dash兼容性问题就很有警示性。引用[1][2][3]都明确提到了三种调用方式:fork/exec/source。用户的需求属于路径调用范畴,所以重点放在fork方式上,也就是最基本的./path/to/script.sh模式。不过考虑到用户调用的是其他目录,需要强调绝对路径和相对路径的区别,尤其是../这种上级目录表示法。啊,引用[4]提到的权限问题特别关键,很多新手会忽略。还有shebang的兼容性问题,这个必须单独列出来说明,避免用户掉坑。对于具体调用方法,准备这样组织:先用最简单直接的./相对路径示例,比如调用父目录脚本用../script.sh再补充绝对路径调用,这样更稳妥重点提醒执行权限和shebang,这两个是高频踩雷点最后补充exec和source的特殊场景,避免用户误用用户虽然没有问,但隐含需求可能是想获取被调用脚本的返回值?引用[3]提到source方式会共享变量,这个进阶特性也值得提一下。不过初次回答还是聚焦基础路径调用,末尾再延伸相关问答比较合适。</think> 好的,在 Shell 脚本调用位于其他目录下的一个 Shell 脚本,核心在于**正确指定目标脚本的路径**。结合提供的引用信息,以下是几种常用的方法及其解释: ### 📌 方法 1:使用相对路径或绝对路径直接调用 (fork 方式) 这是最常见且简单的方式,本质上就是执行一个命令,该命令是目标脚本的路径。 * **格式:** ```bash /path/to/other_script.sh [arguments] # 使用绝对路径 ``` ```bash ../other_dir/other_script.sh [arguments] # 使用相对路径(基于当前脚本的位置) ``` * **说明 (参考引用 [1][2][3]):** * **fork:** 这种方式会启动一个新的**子 Shell 进程**来执行目标脚本 (`other_script.sh`)。 * **环境继承:** 子进程会**继承**当前脚本(父进程)的所有环境变量。 * **环境隔离:** 在子脚本中设置的变量、改变的环境等,**不会影响**父脚本的环境。 * **控制权:** 父脚本会**等待**子脚本执行完毕后,才继续运行后续命令。 * **相对路径:** 起点是**当前脚本运行时所在的目录**(通常是脚本所在目录,但启动脚本时如果使用了相对路径如 `./myscript.sh`,则工作目录是执行命令的位置)。确保路径关系正确是关键。 * **绝对路径:** 最可靠的方式,无论父脚本在哪被执行。💯 * **示例:** ```bash # 假设当前脚本在 /home/user/project/bin # 调用同目录下的 cleanup.sh ./cleanup.sh # 调用上层目录 tools 下的 deploy.sh (使用相对路径) ../tools/deploy.sh # 调用绝对路径为 /opt/app/scripts/backup.sh 的脚本 /opt/app/scripts/backup.sh ``` ### 📌 方法 2:使用 `source` 或 `.` 命令调用 (source 方式) * **格式:** ```bash source /path/to/other_script.sh [arguments] # 使用绝对路径 ``` ```bash . /path/to/other_script.sh [arguments] # . 是 source 的简写(注意点号后面必须有空格) ``` * **说明 (参考引用 [1][3]):** * **source:** 这种方式**不会创建新进程**。目标脚本 (`other_script.sh`) 的内容是在**当前 Shell 进程**中直接执行的。 * **环境共享:** 目标脚本中定义的变量、函数以及对环境变量的改变,在执行完毕后会**保留并直接影响当前脚本(父脚本)**。因为它们运行在同一个进程中。 * **用途:** 当你希望目标脚本中的函数或变量设置能在父脚本中直接使用时(例如读取配置文件、定义公共函数库)。🙋‍♀️ * **不需要执行权限:** 因为是通过 `source` 命令读取文件内容并执行,所以目标脚本文件本身**不需要具有可执行权限 (`x`)**。只需要有读 (`r`) 权限。但这是一种容易被忽略的特例,不符合常规脚本执行习惯。 * **示例:** ```bash # 假设需要加载一个配置文件,其变量需要在当前脚本中使用 source /home/user/project/config/env.cfg # 或者加载一个包含公共函数的脚本库 . /opt/app/lib/common_functions.sh ``` ### 📌 方法 3:使用 `exec` 命令调用 (exec 方式) * **格式:** ```bash exec /path/to/other_script.sh [arguments] # 使用绝对路径 ``` * **说明 (参考引用 [1][2]):** * **exec:** 这种方式使用 `exec` 命令执行目标脚本。关键在于 `exec` 会用目标脚本**替换当前 Shell 进程**。 * **进程替换:** 当前的 Shell 进程(运行父脚本的那个)会被终止,其进程 ID (`PID`) **保持不变但被目标脚本 (`other_script.sh`)** 完全取代。 * **无返回:** 父脚本的后续代码**永远不会被执行**。目标脚本执行结束后,控制权直接返回给调用脚本的上级 Shell终端,而不是父脚本本身。 * **环境影响:** 类似于 `source`,但更加极端。因为它是替换进程,父脚本进程环境会消失,最终环境由目标脚本的执行决定。⚠️ * **用途:** 相对不常用,主要用于特定的场景,如需要在指定环境中完全切换进程(例如在登录 Shell 中加载用户配置后启动一个 Shell)。 * **示例:** ```bash # 在当前脚本中(通常是在末尾),需要完全切换到一个脚本环境且父脚本无需继续执行时 exec /opt/terminal/start_session.sh ``` ### 🛠 关键注意事项 (参考引用 [2][3][4]) 1. **执行权限:** 对于使用直接路径调用 (`./script.sh`, `/path/script.sh`) 和 `exec` 方式,被调用的目标脚本文件**必须具有可执行权限 (`x`)**。 * 使用 `chmod +x /path/to/other_script.sh` 添加权限。 2. **Shebang (`#!`):** 目标脚本的第一行应有正确的 shebang (如 `#!/bin/bash`, `#!/bin/sh`),告知系统应该用哪个解释器来执行它。这有助于确保兼容性(特别是引用 [4] 提到 `sh` 可能是 `dash` 而非 `bash`)。 3. **路径分隔符:** 使用正斜杠 `/` 作为路径分隔符。 4. **路径正确性:** * **相对路径:** 容易出错,尤其是脚本被从不同目录启动时。务必理解相对路径的起点是运行脚本时的*工作目录*。 * **绝对路径:** 最健壮、可靠,推荐在可能的情况下使用。 5. **参数传递:** 所有方法都支持在脚本路径后面添加参数,传递给目标脚本(如 `../tools/run.sh arg1 arg2`)。 6. **`. source` 与环境变量:** 如果需要目标脚本的变量*影响*父脚本环境,用 `source` 或 `.`。如果希望两个脚本环境*隔离*,用直接路径调用或 `exec`(后者的隔离方式非常彻底)。 7. **错误处理 (引用 [5]):** 复杂脚本调用(特别是涉及数据库操作如引用 [5] 中的 `psql`)需要注意错误处理(`$?` 检查返回值,`set -e` 等)。 ### 📖 总结 (哪种方式更适合调用其他目录的脚本?) 对于最常见需求——在一个脚本中运行一个目录下的脚本,并等待其完成后继续运行父脚本,**使用完整路径(绝对或相对路径)直接调用是最直观、最推荐的方法**。 * 🟢 **优点:** 流程清晰(父->子->父回),环境隔离安全(子脚本修改不影响父),使用方式最符合脚本执行的直觉。 * **何时选其他:** * 如果明确需要目标脚本的变量/函数定义在父脚本中可用,用 `source`/`.`。🧠 * 如果父脚本的任务就是用目标脚本完全替代自己且无需后续执行,用 `exec`(场景相对少见)。 ### 📌 相关问题 1. **如何在调用其他目录脚本后检查它的退出状态 (`$?`)?** (基于引用方法1的直接调用方式) 2. **使用 `source` 调用其他目录下的配置脚本有什么优缺点?** (扩展引用[3]关于source共享环境的说明) 3. **如何确保相对路径在不同工作目录下执行父脚本时仍然有效?** (深入探讨相对路径的陷阱) 4. **在 `source` 调用脚本中执行 `exit` 会发生什么? 对父脚本有什么影响?** (比较 `source`, 直接调用, `exec` 方式下 `exit` 的不同效果) 5. **如果在被调用的外部脚本中需要用到父脚本的某些特定变量,有哪些安全的传递方式?** (环境变量、参数、临时文件等) 6. **引用 [4] 中提到的 `sh` vs `bash` vs `dash` 问题,如何避免目标脚本因解释器不同导致错误?** (Shebang 的重要性及显式调用解释器 `/bin/bash script.sh`) [^1]: 引用主要阐述了调用方式分类(fork, exec, source)及使用连接器进行脚本顺序控制。 [^2]: 引用详细说明了 fork 方式的执行过程和父子 Shell 环境变量的继承关系。 [^3]: 引用明确了 source (或 .) 和直接调用(/bin/bash, sh)的关键区别:是否在相同进程执行,以及变量和函数的可见性。 [^4]: 引用强调了执行权限和 shebang 的重要性,并特别指出了 sh 在 Ubuntu 上默认链接到 dash 以及与 bash 的语法差异(如 [[ ]] 支持)。 [^5]: 引用展示了在 shell 脚本调用数据库代码块(`psql <<EOF ... EOF`)的复杂示例,提示了错误处理的重要性。
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值