目录
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/shecho"test echo"
posix.sh是测试posix模式下source命令的测试脚本
#!/bin/sh#posix模式,文件名不包括斜杠,则使用PATH变量搜索文件,#没有搜索到,发生错误,在posix模式中就会直接退出,不再执行接下来的命令source local_cmd.sh
echo"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.sh
echo"return value=$#"
运行结果:
./no_posix.sh
testechoreturnvalue=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环境中, 从文件名中读取并执行命令.
这句话很重要,包含有几层意思,接下来一一解读.
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
sourcetest 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.bashrc
fielseif["$(id-u)"-eq0];thenPS1='# 'elsePS1='$ 'fififi#根据情况执行系统脚本if[-d /etc/profile.d ];thenforiin /etc/profile.d/*.sh;doif[-r$i];then.$ifidoneunset i
fi#用户可以在这里制定环境变量exportPATH="/opt/cmake-3.27.8-linux-x86_64/bin:$PATH"exportPATH="/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"fifi# 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 anythingcase $- in
*i*);;
*)return;;esac# don't put duplicate lines or lines starting with space in the history.# See bash(1) for more optionsHISTCONTROL=ignoreboth
# append to the history file, don't overwrite itshopt-s histappend
# for setting history length see HISTSIZE and HISTFILESIZE in bash(1)HISTSIZE=1000HISTFILESIZE=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"in
xterm-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=yes
elsecolor_prompt=fifiif["$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\$ 'fiunset color_prompt force_color_prompt
# If this is an xterm set the title to user@host:dircase"$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 aliasesif[-x /usr/bin/dircolors ];thentest-r ~/.dircolors &&eval"$(dircolors-b ~/.dircolors)"||eval"$(dircolors-b)"aliasls='ls --color=auto'#alias dir='dir --color=auto'#alias vdir='vdir --color=auto'aliasgrep='grep --color=auto'aliasfgrep='fgrep --color=auto'aliasegrep='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 aliasesaliasll='ls -alF'aliasla='ls -A'aliasl='ls -CF'# Add an "alert" alias for long running commands. Use like so:# sleep 10; alertaliasalert='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_completion
elif[-f /etc/bash_completion ];then. /etc/bash_completion
fifi#用户可以定制PATH=$HOME/.local/bin:$PATH
/etc/profile,~/.profile,~/.bashrc
脚本在用户登录时都会自动执行,并且设置的环境变量和函数都会在登录后保存在终端里.
如果用户修改了这三个脚本,可以不重启,不退出,使用source命令立即生效.
source /etc/profile
source ~/.profile
source ~/.bashrc
1.3.3.从文件名中读取并执行命令
从文件名中读取并执行命令,可以理解为source后面接的文件名对应的文件可以不是脚本,只要是可读权限的文本文件即可.
例如,建立如下文件,只给文件可读权限
touch readme.txt
vim readme.txt
TEST_VAR_123=hello_world
echo"test source command!"chmoda=r readme.txt
ll
-r--r--r-- 1 lei lei 534月 1222:01 readme.txt
source readme.txt
testsource 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-xLANG="en_US.UTF-8"declare-xLC_ADDRESS="zh_CN.UTF-8"declare-xLC_IDENTIFICATION="zh_CN.UTF-8"declare-xLC_MEASUREMENT="zh_CN.UTF-8"declare-xLC_MONETARY="zh_CN.UTF-8"declare-xLC_NAME="zh_CN.UTF-8"declare-xLC_NUMERIC="zh_CN.UTF-8"declare-xLC_PAPER="zh_CN.UTF-8"declare-xLC_TELEPHONE="zh_CN.UTF-8"... ...
2.2.验证变量和函数导出功能
export_test/
├── child_shell.sh
└── paren_shell.sh
paren_shell.sh
#!/bin/bashPARENT_EXPORT_TEST_VAR=parent_export_value
export PARENT_EXPORT_TEST_VAR
PARENT_NOEXPORT_TEST_VAR=parent_noexport_value
functiontestFunc(){echo"testFunc run!"}export-f testFunc
sh child_shell.sh
export-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.sh
PARENT_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.sh
和
pri_child_shell.sh
source_parent_shell.sh
#!/bin/bashSOURCE_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-xSHELL="/bin/bash"declare-xSHLVL="1"declare-xSOURCE_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.sh
SOURCE_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/bashecho"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命令导出.
版权归原作者 fedorayang 所有, 如有侵权,请联系我们删除。