LinuxShell编程中source和export命令

devtools/2024/12/22 20:00:50/

目录

  • 1.source命令
    • 1.1.POSIX模式
      • 1.1.1.验证POSIX模式执行情况
    • 1.2.source命令表示形式的历史由来
    • 1.3.source命令解读
      • 1.3.1.在当前的shell环境中
      • 1.3.2.source命令的常用用途
      • 1.3.3.从文件名中读取并执行命令
  • 2.export命令
    • 2.1.显示当前终端已经导出的函数和环境变量
    • 2.2.验证变量和函数导出功能
  • 3.source命令和export命令的结合
  • 4.在profile,.bashrc等脚本中变量是否需要明确导出
  • 5.结论

1.source命令

source命令是shell的内部命令,和. 命令相同.

注意这里的.+空格+文件名[参数],需要和./file.sh不同.

source命令格式:

source 文件名 [参数]

在当前的shell环境中, 从文件名中读取并执行命令.

如果文件名不包括斜杠,则使用 PATH 变量去搜索文件. 如果Bash不是在POSIX模式下运行, 则在$PATH中找不到后就会在当前目录中搜索.

如果提供了参数, 它们就成为执行文件名时的位置参数; 否则, 位置参数不会被改变.

返回状态是最后一个被执行命令的退出状态; 如果没有命令被执行,则返回零. 如果文件名没有找到, 或者不能读取, 返回状态就是非零值.

1.1.POSIX模式

#脚本第一行如下所示是执行在POSIX模式下
#!/bin/sh
#POSIX模式是符合POSIX规范的运行模式
#POSIX模式也可以如下指定,在脚本第一行
#!/bin/bash --posix
#脚本第一行如下所示是执行在非POSIX模式下,也可以认为是扩展模式,是POSIX的增强模式
#!/bin/bash

1.1.1.验证POSIX模式执行情况

准备文件夹如下:

└── posix_mode_test├── local_cmd.sh├── no_posix.sh└── posix.sh

local_cmd.sh是测试脚本命令,内容如下:

#!/bin/sh
echo "test echo"

posix.sh是测试posix模式下source命令的测试脚本

#!/bin/sh
#posix模式,文件名不包括斜杠,则使用PATH变量搜索文件,
#没有搜索到,发生错误,在posix模式中就会直接退出,不再执行接下来的命令source local_cmd.shecho "return value=$#"

运行结果:

./posix.sh 
./posix.sh: line 5: source: local_cmd.sh: file not found

可以看到运行到第5行发生错误就退出了.

no_posix.sh是非posix模式下source命令的测试脚本

#!/bin/bash
#非posix模式,文件名不包括斜杠,则使用PATH变量搜索文件,没有搜索到,
#就在当前目录中搜索,在当前目录中可以搜索到local_cmd.sh文件,执行文件,显示返回值并退出source local_cmd.shecho "return value=$#"

运行结果:

./no_posix.sh 
test echo
return value=0

可以看到运行结果和预期一样.

其他情况验证:

#1.第一行如下语句,也是posix模式
#!/bin/bash --posix#2.第一行没有指定"#!/bin/sh"或"#!/bin/bash",默认情况下是非posix模式

1.2.source命令表示形式的历史由来

source filename表示形式来自Cshell;

. filename表示形式来自Bourne Shell.

为了兼容用户的习惯,使用了两种表示形式.

1.3.source命令解读

在当前的shell环境中, 从文件名中读取并执行命令.

这句话很重要,包含有几层意思,接下来一一解读.

shell_128">1.3.1.在当前的shell环境中

要想理解这个条件,先必须了解几种常用的shell脚本执行方式:

1.工作目录执行

先进入到脚本所在的目录(此时, 称为工作目录), 然后使用 ./脚本方式执行.

脚本需要具有执行权限才可以.

2.绝对路径执行

/home/shell/source_cmd/posix_mode_test/local_cmd.sh

也需要脚本具有可执行权限.

3.使用sh或bash命令执行

sh local_cmd.sh
#或者
bash local_cmd.sh

脚本只需要具有读权限即可,sh或bash把脚本当作参数,读取脚本内容并执行.

4.shell环境执行

也就是这里所说的**“在当前的shell环境中”**

source local_cmd.sh

上述4种shell脚本执行方式可以归为两类:

一, 1-3执行内部原理一样,会新建一个子shell,在子shell中执行脚本里面的语句,该子shell继承父shell的环境变量,但子shell新建的,改变的变量不会被带回父shell.

二,4为另一类,source命令其实只是简单地读取脚本里面的语句依次在当前shell里面执行, 没有建立新的子shell. 那么脚本里面所有新建,改变变量的语句都会保存在当前shell里面.

验证这两类情况.

#文件目录结构
verify_shell_run/
├── child_run_test.sh
└── source_run_test.sh

child_run_test.sh 内容

#!/bin/bashCHILD_VAR=child_context
echo "child test run!"

source_run_test.sh 内容

#!/bin/bashPARENT_VAR=parent_context
echo "source test run!"

验证情况:

#运行第一类情况
sh child_run_test.sh
child test run!
#打印变量值
echo $CHILD_VAR
#显示内容为空#运行第二类情况
source source_run_test.sh 
source test run!
#打印变量值
echo $PARENT_VAR
parent_context
#变量在当前shell中有效

验证结果符合预期.

1.3.2.source命令的常用用途

根据上一点分析:source命令一般的用途是,打开一个shell终端,在shell终端中执行source脚本,可以设置环境变量和函数,这些环境变量和

函数就会在当前shell终端中一直存在,建立shell终端的环境,方便工作.

Linux登录过程使用source命令建立用户环境,以下简略描述一下过程:

1.每个终端登录时,先执行/etc/profile脚本

 # /etc/profile: system-wide .profile file for the Bourne shell (sh(1))# and Bourne compatible shells (bash(1), ksh(1), ash(1), ...).if [ "${PS1-}" ]; thenif [ "${BASH-}" ] && [ "$BASH" != "/bin/sh" ]; then# The file bash.bashrc already sets the default PS1.# PS1='\h:\w\$ 'if [ -f /etc/bash.bashrc ]; then#根据情况执行系统脚本. /etc/bash.bashrcfi  elseif [ "$(id -u)" -eq 0 ]; thenPS1='# 'elsePS1='$ 'fi  fi  fi#根据情况执行系统脚本if [ -d /etc/profile.d ]; thenfor i in /etc/profile.d/*.sh; doif [ -r $i ]; then. $ifi  doneunset ifi#用户可以在这里制定环境变量export PATH="/opt/cmake-3.27.8-linux-x86_64/bin:$PATH"export PATH="/opt/node-v20.11.0-linux-x64/bin:$PATH"

2.执行用户目录下的.profile脚本

 # ~/.profile: executed by the command interpreter for login shells.# This file is not read by bash(1), if ~/.bash_profile or ~/.bash_login# exists.# see /usr/share/doc/bash/examples/startup-files for examples.# the files are located in the bash-doc package.# the default umask is set in /etc/profile; for setting the umask# for ssh logins, install and configure the libpam-umask package.#umask 022# if running bashif [ -n "$BASH_VERSION" ]; then# include .bashrc if it existsif [ -f "$HOME/.bashrc" ]; then#执行用户目录下的.bashrc脚本. "$HOME/.bashrc"fi  fi# set PATH so it includes user's private bin if it existsif [ -d "$HOME/bin" ] ; thenPATH="$HOME/bin:$PATH"fi# set PATH so it includes user's private bin if it existsif [ -d "$HOME/.local/bin" ] ; thenPATH="$HOME/.local/bin:$PATH"fi#用户自定义设置环境变量和函数source $HOME/setenv_qt.sh

3…执行用户目录下的.bashrc脚本

在用户目录下的.profile脚本的第16行调用用户目录下的.bashrc脚本,如上所示.

# ~/.bashrc: executed by bash(1) for non-login shells.
# see /usr/share/doc/bash/examples/startup-files (in the package bash-doc)
# for examples# If not running interactively, don't do anything
case $- in*i*) ;;*) return;;
esac# don't put duplicate lines or lines starting with space in the history.
# See bash(1) for more options
HISTCONTROL=ignoreboth# append to the history file, don't overwrite it
shopt -s histappend# for setting history length see HISTSIZE and HISTFILESIZE in bash(1)
HISTSIZE=1000
HISTFILESIZE=2000# check the window size after each command and, if necessary,
# update the values of LINES and COLUMNS.
shopt -s checkwinsize# If set, the pattern "**" used in a pathname expansion context will
# match all files and zero or more directories and subdirectories.
#shopt -s globstar# make less more friendly for non-text input files, see lesspipe(1)
[ -x /usr/bin/lesspipe ] && eval "$(SHELL=/bin/sh lesspipe)"# set variable identifying the chroot you work in (used in the prompt below)
if [ -z "${debian_chroot:-}" ] && [ -r /etc/debian_chroot ]; thendebian_chroot=$(cat /etc/debian_chroot)
fi# set a fancy prompt (non-color, unless we know we "want" color)
case "$TERM" inxterm-color|*-256color) color_prompt=yes;;
esac# uncomment for a colored prompt, if the terminal has the capability; turned
# off by default to not distract the user: the focus in a terminal window
# should be on the output of commands, not on the prompt
#force_color_prompt=yesif [ -n "$force_color_prompt" ]; thenif [ -x /usr/bin/tput ] && tput setaf 1 >&/dev/null; then# We have color support; assume it's compliant with Ecma-48# (ISO/IEC-6429). (Lack of such support is extremely rare, and such# a case would tend to support setf rather than setaf.)color_prompt=yeselsecolor_prompt=fi
fi
if [ "$color_prompt" = yes ]; thenPS1='${debian_chroot:+($debian_chroot)}\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ '
elsePS1='${debian_chroot:+($debian_chroot)}\u@\h:\w\$ '
fi
unset color_prompt force_color_prompt# If this is an xterm set the title to user@host:dir
case "$TERM" in
xterm*|rxvt*)PS1="\[\e]0;${debian_chroot:+($debian_chroot)}\u@\h: \w\a\]$PS1";;
*);;
esac# enable color support of ls and also add handy aliases
if [ -x /usr/bin/dircolors ]; thentest -r ~/.dircolors && eval "$(dircolors -b ~/.dircolors)" || eval "$(dircolors -b)"alias ls='ls --color=auto'#alias dir='dir --color=auto'#alias vdir='vdir --color=auto'alias grep='grep --color=auto'alias fgrep='fgrep --color=auto'alias egrep='egrep --color=auto'
fi# colored GCC warnings and errors
#export GCC_COLORS='error=01;31:warning=01;35:note=01;36:caret=01;32:locus=01:quote=01'# some more ls aliases
alias ll='ls -alF'
alias la='ls -A'
alias l='ls -CF'# Add an "alert" alias for long running commands.  Use like so:
#   sleep 10; alert
alias alert='notify-send --urgency=low -i "$([ $? = 0 ] && echo terminal || echo error)" "$(history|tail -n1|sed -e '\''s/^\s*[0-9]\+\s*//;s/[;&|]\s*alert$//'\'')"'# Alias definitions.
# You may want to put all your additions into a separate file like
# ~/.bash_aliases, instead of adding them here directly.
# See /usr/share/doc/bash-doc/examples in the bash-doc packageif [ -f ~/.bash_aliases ]; then. ~/.bash_aliases
fi# enable programmable completion features (you don't need to enable
# this, if it's already enabled in /etc/bash.bashrc and /etc/profile
# sources /etc/bash.bashrc).
if ! shopt -oq posix; thenif [ -f /usr/share/bash-completion/bash_completion ]; then. /usr/share/bash-completion/bash_completionelif [ -f /etc/bash_completion ]; then. /etc/bash_completionfi
fi
#用户可以定制
PATH=$HOME/.local/bin:$PATH

/etc/profile,~/.profile,~/.bashrc脚本在用户登录时都会自动执行,并且设置的环境变量和函数都会在登录后保存在终端里.

如果用户修改了这三个脚本,可以不重启,不退出,使用source命令立即生效.

source /etc/profile
source ~/.profile
source ~/.bashrc

1.3.3.从文件名中读取并执行命令

从文件名中读取并执行命令,可以理解为source后面接的文件名对应的文件可以不是脚本,只要是可读权限的文本文件即可.

例如,建立如下文件,只给文件可读权限

touch readme.txtvim readme.txtTEST_VAR_123=hello_worldecho "test source command!"chmod a=r readme.txt
ll
-r--r--r-- 1 lei lei   53  412 22:01 readme.txtsource readme.txt 
test source command!echo $TEST_VAR_123
hello_world

2.export命令

调用格式

 export [-fn][-p][名称[=]]

把每个名称都传到子进程的环境中.

如果给定了"-f"选项(助记词:Function函数),则名称就是shell函数;否则,它就是shell变量.

"-n"选项(助记词:No不)表示不再把名称导出到子进程.

如果没有给定名称,或者给定了"-p"选项(助记词:Pretty-Print格式化打印),则显示已经导出的名称列表.

"-p"选项能够把输出格式化成可以重新作为输入的形式.

如果名称后面是"=值"的形式,那么这个值就会赋给名称.

这个命令的返回状态是零,除非指定了无效的选项,或者其中一个名称不是有效的shell变量名,或者"-f"指定的不是一个shell函数的名称.

2.1.显示当前终端已经导出的函数和环境变量

export... ...
declare -x LANG="en_US.UTF-8"
declare -x LC_ADDRESS="zh_CN.UTF-8"
declare -x LC_IDENTIFICATION="zh_CN.UTF-8"
declare -x LC_MEASUREMENT="zh_CN.UTF-8"
declare -x LC_MONETARY="zh_CN.UTF-8"
declare -x LC_NAME="zh_CN.UTF-8"
declare -x LC_NUMERIC="zh_CN.UTF-8"
declare -x LC_PAPER="zh_CN.UTF-8"
declare -x LC_TELEPHONE="zh_CN.UTF-8"
... ...

2.2.验证变量和函数导出功能

export_test/
├── child_shell.sh
└── paren_shell.sh

paren_shell.sh

#!/bin/bash
PARENT_EXPORT_TEST_VAR=parent_export_value
export PARENT_EXPORT_TEST_VAR
PARENT_NOEXPORT_TEST_VAR=parent_noexport_value
function testFunc()
{echo "testFunc run!"
}
export -f testFunc
sh child_shell.shexport -n PARENT_EXPORT_TEST_VAR
export -nf testFunc
sh child_shell.sh

child_shell.sh

#!/bin/bashecho "PARENT_EXPORT_TEST_VAR=$PARENT_EXPORT_TEST_VAR"
echo "PARENT_NOEXPORT_TEST_VAR=$PARENT_NOEXPORT_TEST_VAR"
testFunc

运行结果:

sh paren_shell.shPARENT_EXPORT_TEST_VAR=parent_export_value
PARENT_NOEXPORT_TEST_VAR=
testFunc run!
PARENT_EXPORT_TEST_VAR=
PARENT_NOEXPORT_TEST_VAR=
child_shell.sh: line 5: testFunc: command not found

可以看到导出的变量在子shell中有值,没有导出的变量在子shell中没有值,导出的函数在子shell中可以运行.

在第二次调用子shell之前取消导出,结果变量都为空,函数没有定义.

3.source命令和export命令的结合

准备两个脚本source_parent_shell.shpri_child_shell.sh

source_parent_shell.sh

#!/bin/bash
SOURCE_PARENT_EXPORT_TEST_VAR=source_parent_export_value
export SOURCE_PARENT_EXPORT_TEST_VAR
SOURCE_PARENT_NOEXPORT_TEST_VAR=source_parent_noexport_value

pri_child_shell.sh

#!/bin/bashecho "SOURCE_PARENT_EXPORT_TEST_VAR=$SOURCE_PARENT_EXPORT_TEST_VAR"
echo "SOURCE_PARENT_NOEXPORT_TEST_VAR=$SOURCE_PARENT_NOEXPORT_TEST_VAR"

source方式运行source_parent_shell.sh

source ./source_parent_shell.sh

在当前shell中查看变

echo $SOURCE_PARENT_EXPORT_TEST_VAR
source_parent_export_value
echo $SOURCE_PARENT_NOEXPORT_TEST_VAR 
source_parent_noexport_value#可以看到两个变量在当前shell中都有效

使用export查看当前shell中导出的变量

export... ...
declare -x SHELL="/bin/bash"
declare -x SHLVL="1"
declare -x SOURCE_PARENT_EXPORT_TEST_VAR="source_parent_export_value"
... ...#可以查询到SOURCE_PARENT_EXPORT_TEST_VAR被导出
#SOURCE_PARENT_NOEXPORT_TEST_VAR没有被导出

以子进程方式运行pri_child_shell.sh

bash ./pri_child_shell.shSOURCE_PARENT_EXPORT_TEST_VAR=source_parent_export_value
SOURCE_PARENT_NOEXPORT_TEST_VAR=#可以看到在子进程中SOURCE_PARENT_NOEXPORT_TEST_VAR变量不能被访问,即使在当前shell中可以访问
#一定要明确使用export导出才能在子进程中访问

4.在profile,.bashrc等脚本中变量是否需要明确导出

#在/etc/profile中定义环境变量,不导出
GLOB_PROFILE_VAR=glob_profile_value
#在~/.profile中定义环境变量,不导出
GLOB_HOME_VAR=glob_home_value

准备一个测试文件system_bash_script_test.sh

#!/bin/bash
echo "GLOB_PROFILE_VAR=$GLOB_PROFILE_VAR"
echo "GLOB_HOME_VAR=$GLOB_HOME_VAR"

以子进程方式运行

./system_bash_script_test.sh 
GLOB_PROFILE_VAR=
GLOB_HOME_VAR=

shell环境方式执行

source ./system_bash_script_test.sh 
GLOB_PROFILE_VAR=glob_profile_value
GLOB_HOME_VAR=glob_home_value

也是只能在当前shell中访问,不能传输到子进程中,和普通脚本原理一样,没有特殊的地方.

5.结论

source命令可以改变当前shell中的环境变量,要在子shell访问必须使用export命令导出.


http://www.ppmy.cn/devtools/2054.html

相关文章

多模态之ALBEF—先对齐后融合,利用动量蒸馏学习视觉语言模型表征,学习细节理解与论文详细阅读:Align before Fuse

Align before Fuse: Vision and Language Representation Learning with Momentum Distillation (ALBEF)在融合之前对齐:利用动量蒸馏进行视觉与语言表示学习 Paper: arxiv.org/pdf/2107.07651.pdf Github: https://github.com/salesforce/…

Java 的数据类型

文章目录 整型Java 和 C、C 在数据类型上的区别数据类型的后缀 浮点型十六进制表示浮点数值 字符型转义序列布尔型 Java 是一种强类型语言。 这意味着必须为每一个变量声明一种类型。 Java 中的基本数据类型分为8种 4种为:整型。 2种为:浮点类型。 1种为…

纯css实现switch开关

代码比较简单&#xff0c;有需要直接在下边粘贴使用吧~ html: <div class"switch-box"><input id"switch" type"checkbox"><label></label></div> css&#xff1a; .switch-box {position: relative;height: 25px…

Github 2024-04-16Python开源项目日报 Top10

根据Github Trendings的统计,今日(2024-04-16统计)共有10个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量Python项目10TypeScript项目1Vue项目1系统设计指南 创建周期:2507 天开发语言:Python协议类型:OtherStar数量:241693 个Fork数量:42010 次…

「前所未有!」服务网格与 Envoy Gateway,客户端可用性和弹性双提升,完美演绎 IT 新时代!

目录 如何从客户端角度思考服务的可用性和弹性 每个服务都有一个“网关” 以三个九的价格获得五个九的高可用能力&#xff1a;一个真实的案例 服务网格中的客户端负载均衡&#xff1a;超越组件之和 重试 异常检测 断路器 超时 限流 低成本下的高感知可用性 下一步 参…

第十五届蓝桥杯大赛软件赛省赛(Java 大学B组)

蓝桥杯 2024年省赛真题 Java 大学B组 试题 A: 报数游戏试题 B: 类斐波那契循环数试题 C: 分布式队列 在找工作&#xff0c;随便写写&#xff0c;安定下来再把去年国赛题解补上 试题 A: 报数游戏 本题总分&#xff1a; 5 5 5 分 【问题描述】 小蓝和朋友们在玩一个报数游戏。由…

【uniapp】 合成海报组件

之前公司的同事写过一个微信小程序用的 合成海报的组件 非常十分好用 最近的项目是uni的 把组件改造一下也可以用 记录一下 <template><view><canvas type"2d" class"_mycanvas" id"my-canvas" canvas-id"my-canvas" …

学习ArkTS -- 常用组件使用

学习ArkTS 使用Deveco studio写ArkTSImage: 图片显示组件1.声明Image组件并设置图片源2. 添加图片属性 Text: 文本显示组件1. 声明Text组件并设置文本内容2. 添加文本属性 TextInput&#xff1a;文本输入框1. 声明TextInput2. 添加属性和事件 Button 组件1. 声明Button组件&…