在当前bash(sh)中执行脚本和注册函数

在研究《管理Python虚拟环境的脚本》时,我们使用了source指令而没有使用sh或者bash来执行脚本,就是因为source指令可以让脚本在当前bash(sh)中执行;而sh或者bash则会新启动一个bash来执行。

我们可以通过下面这个脚本做测试

bash 复制代码
# test.sh
# 用一个数组保存进程ID和进程名
processInfo=()

# 查找父进程的进程号
findParentID() {
    if [ $1 = $2 ]; then
        # 如果父进程号等于目标进程号,说明已经找到了父进程
        # 打印所有进程信息
        echo "processInfo: ${processInfo[@]}"
        return
    else
        # 获取当前进程的父进程号
        parentID=$(ps -o ppid= $1)
        # 获取父进程的名字
        parentName=$(ps -o comm= $parentID)

        # 将父进程号和父进程名保存到数组中
        processInfo+=($parentID $parentName)

        findParentID $parentID $2
    fi
}

currentName=$(ps -o comm= $$)
processInfo+=($$ $currentName)
findParentID $$ $1

bash

bash 复制代码
bash test.sh $$

processInfo: 45322 bash 40883 bash

当前bash的进程ID是40883,新启动的bash的进程ID是45322。

source

bash 复制代码
source test.sh $$

processInfo: 40883 bash

可以见得没有启动新的bash程序。

source还可以让自动注册脚本中的函数。

比如上面指令让脚本中的findParentID方法可以直接被使用。

bash 复制代码
findParentID $$ $$

processInfo: 40883 bash

相似的应用在Python虚拟环境中也有体现。

比如我们启动一个虚拟环境,使用下面的命令

bash 复制代码
source .env/bin/activate

而退出虚拟环境的方法deactivate则注册在.env/bin/activate文件中

bash 复制代码
# This file must be used with "source bin/activate" *from bash*
# you cannot run it directly

deactivate () {
    # reset old environment variables
    if [ -n "${_OLD_VIRTUAL_PATH:-}" ] ; then
        PATH="${_OLD_VIRTUAL_PATH:-}"
        export PATH
        unset _OLD_VIRTUAL_PATH
    fi
    if [ -n "${_OLD_VIRTUAL_PYTHONHOME:-}" ] ; then
        PYTHONHOME="${_OLD_VIRTUAL_PYTHONHOME:-}"
        export PYTHONHOME
        unset _OLD_VIRTUAL_PYTHONHOME
    fi

    # This should detect bash and zsh, which have a hash command that must
    # be called to get it to forget past commands.  Without forgetting
    # past commands the $PATH changes we made may not be respected
    if [ -n "${BASH:-}" -o -n "${ZSH_VERSION:-}" ] ; then
        hash -r 2> /dev/null
    fi

    if [ -n "${_OLD_VIRTUAL_PS1:-}" ] ; then
        PS1="${_OLD_VIRTUAL_PS1:-}"
        export PS1
        unset _OLD_VIRTUAL_PS1
    fi

    unset VIRTUAL_ENV
    unset VIRTUAL_ENV_PROMPT
    if [ ! "${1:-}" = "nondestructive" ] ; then
    # Self destruct!
        unset -f deactivate
    fi
}

# unset irrelevant variables
deactivate nondestructive

VIRTUAL_ENV="/home/fangliang/numpy-example/.env"
export VIRTUAL_ENV

_OLD_VIRTUAL_PATH="$PATH"
PATH="$VIRTUAL_ENV/bin:$PATH"
export PATH

# unset PYTHONHOME if set
# this will fail if PYTHONHOME is set to the empty string (which is bad anyway)
# could use `if (set -u; : $PYTHONHOME) ;` in bash
if [ -n "${PYTHONHOME:-}" ] ; then
    _OLD_VIRTUAL_PYTHONHOME="${PYTHONHOME:-}"
    unset PYTHONHOME
fi

if [ -z "${VIRTUAL_ENV_DISABLE_PROMPT:-}" ] ; then
    _OLD_VIRTUAL_PS1="${PS1:-}"
    PS1="(.env) ${PS1:-}"
    export PS1
    VIRTUAL_ENV_PROMPT="(.env) "
    export VIRTUAL_ENV_PROMPT
fi

# This should detect bash and zsh, which have a hash command that must
# be called to get it to forget past commands.  Without forgetting
# past commands the $PATH changes we made may not be respected
if [ -n "${BASH:-}" -o -n "${ZSH_VERSION:-}" ] ; then
    hash -r 2> /dev/null
fi

如果我们使用bash来执行,则因为虚拟环境会在新启动的bash中存在,并会快速退出。回到我们原来的bash中时,已经不是虚拟环境了。相应的deactivate方法也没注册到环境中。

所以如果我们希望脚本对当前bash有所影响,就要使用source去执行脚本;如果不希望影响当前bash,则可以使用bash或者sh去执行。

需要注意的是,bash并不等价于sh。sh(Bourne Shell)是1978年由史蒂夫·伯恩编写的shell;bash(Bourne-Again Shell)是1987年由布莱恩·福克斯为GNU计划编写的Unix shell。主要目标是与POSIX标准保持一致,同时兼顾对sh的兼容,是各种Linux发行版标准配置的Shell。比如上面test.sh使用bash可以正确执行,而sh执行就会报错。

相关推荐
我真会写代码8 小时前
从入门到精通:Java Socket 网络编程实战(含线程池优化)
java·linux·服务器·socket·tcp/ip协议
刘某的Cloud8 小时前
全局禁用ipv6
linux·运维·网络·系统·ipv6
程序辕日记8 小时前
Linux环境docker离线安装教程
linux·docker
噜啦噜啦嘞好8 小时前
生产者消费者模型
linux·开发语言
阿巴~阿巴~8 小时前
HTTP进化史:从0.9到3.0的技术跃迁
linux·服务器·网络·网络协议·http
列逍8 小时前
Linux进程(一)
linux·运维·服务器
Xの哲學8 小时前
Linux内核数据结构:设计哲学与实现机制
linux·服务器·算法·架构·边缘计算
繁华似锦respect9 小时前
Linux - KCP 协议深度解析:原理、与 TCP/UDP 的对比及应用场景
linux·tcp/ip·观察者模式·设计模式·udp
若疆赤云online9 小时前
Ubuntu Dockerfile jar运行安装中文字体
linux·ubuntu·jar
洛可可白9 小时前
Ubuntu 上安装 Docker
linux·ubuntu·docker