Shell - 03 深刻理解export关键字(再也不会犯迷糊)

最近在被Shell脚本中的export关键字所困!记录一下,谈谈自己的理解。
目录:
0、预热:粗略了解Linux父、子进程
1、Shell执行脚本的几种方式
----1.0 source [script_file_name] 执行
----1.1 . [script_file_name]点符号执行
----1.2 指定某个shell来执行
----1.3 相对或绝对路径/script_file_name执行
----1.4 实例
2、export关键字终出场
----2.0 Linux中变量类型知多少
---- 2.1 export实践
0、对Linux父进程、子进程一点点粗略了解 这里仅做我自己的简要理解,因为每一个深究都是一门大学问,没必要,但再次深深感觉到计算机基本组成原理知识的重要性。在此只是为了有助于利于export
【Shell - 03 深刻理解export关键字(再也不会犯迷糊)】Linux是一个多用户多任务的操作系统,必须要支持多个用户同时登录同一个操作系统的操作。当一个用户登录一次时,操作系统就为这个用户创建一个新会话(比如Shell(就是我们常说的【终端】))。
Shell - 03 深刻理解export关键字(再也不会犯迷糊)
文章图片

Linux系统中,进程之间有一个明显的继承关系,所有进程都是 PID 为1的 init 进程后代。内核在系统启动的最后阶段启动 init 进程。该进程读取系统的初始化脚本(initscript)并执行其他的相关程序,最终完成系统启动的整个过程。
用户登陆Linux,就获得一个bash(Shell),之后你的bash(Shell)就是一个独立的进程(Shell 父进程)。之后你在bash(Shell)下面执行的任何命令都是由这个bash所衍生的,那些被执行的命令被称为子进程(Shell子进程)。
子进程只会继承父进程的环境变量,子进程不会继承父进程的自定义变量。那么你原本bash中的自定义变量在进入子进程后就会消失不见,一直到你离开子进程并回到原本的父进程后,这个变量才会出现。除非把自定义变量设置为环境变量 export name
Linux中的进程及进程控制
1、Shell执行脚本的几种方式 分为两大类,4小类。本质上都得指明脚本文件所在路径,即怎么找到它。

1、在当前shell中执行【相对或绝对路径下都行】,这两个本质是一样的 source script_file_name . script_file_name 中间有1个空格 2、在当前shell(父进程)开启一个【子shell(子进程)】中执行, 脚本一旦执行完后子shell环境将随即关闭,然后又回到父shell中,而无法再访问脚本中的变量的(不管有没有export)。 【无论脚本中是否有#!/bin/bash这行,都会开启子shell去执行】 sh script_file_name 相对或绝对路径/script_file_name【若脚本无#!/bin/bash,则会选择系统默认shell执行它】

一个规范的Shell脚本在第一行指出由哪个程序(解释器)来执行脚本的内容,而这一行内容在Linux bash一般为:
#!/bin/bash#或#!/bin/sh#sh为bash的软链接。更规范的写法是bash。

创建一个hello.sh脚本
[root@master Cshell]# pwd /usr/local/src/Cshell [root@master Cshell]# vim hello.sh #!/bin/basha="hello" export MY_VAR="MY_VAR"echo "Hello World!"

1.0 source [script_file_name] 执行
[root@master Cshell]# pwd /usr/local/src/Cshell [cyg@master Cshell]$ source hello.sh #当前工作路径下 Hello World! [cyg@master Cshell]$ cd .. [cyg@master src]$ pwd /usr/local/src [cyg@master src]$ source /usr/local/src/Cshell/hello.sh #绝对路径下 Hello World! [cyg@master src]$ source ./Cshell/hello.sh #相对路径下 Hello World!

常见例子:source /etc/profile,作用是使【设置的针对所有用户(不只是root)登录时都会运行的系统级别的环境变量的配置文件/etc/profile立即生效,而不必注销并重新登录。
1.1 . [script_file_name]点符号执行
[root@master Cshell]# pwd /usr/local/src/Cshell [cyg@master Cshell]$ . hello.sh Hello World! [cyg@master Cshell]$ cd .. [cyg@master src]$ pwd /usr/local/src [cyg@master src]$ . /usr/local/src/Cshell/hello.sh Hello World! [cyg@master src]$ . ./Cshell/hello.sh Hello World!

小结:source.(点符号)功能是:读入脚本并执行脚本
  • 在当前Shell中执行加载并执行的相关脚本文件的命令及语句,而不是产生一个子Shell来执行。所以,在执行脚本后,当前shell可访问脚本中任一变量,即 这些变量成为了当前shell的一部分(都在一个进程里了)
  • 都不需要当前用户有对脚本文件的可执行权限。因为都将脚本文件看作1个参数
  • 两者唯一区别:source是bash内置命令;点符号是source另一名称
[root@master Cshell]# echo $a # a是hello.sh中的一个普通变量[root@master Cshell]# source hello.sh Hello World! [root@master Cshell]# echo $a hello

1.2 指定某个shell来执行
[root@master Cshell]# ll -rw-r--r-- 1 root root67 May 31 16:08 hello.sh [root@master Cshell]# sh hello.sh Hello World!

  • shell有很多种解释器:bash、sh、ash、bsh、csh、ksh、tcsh等;
  • 运行格式:[bash] [file_name.sh]。由这里指定的shell运行这个脚本,与Linux登录的哪个用户也无关(如root、或其他用户);
  • 上述实例:是将hello.sh文件名作为一个参数传递给sh命令来执行。即 此时不是hello.sh自己来执行,而是被sh所调用来执行,所以甚至可以不要
    • 文件执行权限(无需当前用户有对它的可执行权限
    • 第一行#!/bin/bash,指定bash路径
1.3 相对或绝对路径/script_file_name执行
  • 当前用户需要有对该文件的可执行权限:这种方式执行脚本文件,就意味着该文件本身是个具备可执行权限的文件,即 它自己就是个可执行文件。否则是执行不了。
  • 加上相对或绝对路径的目的是为了让bash找到脚本文件在哪里。因为当前工作目录(pwd)可能不在执行程序默认的搜索路径之列,即 不在环境变量PATH的内容之中(echo $PATH可查看)。
[cyg@master src]$ pwd /usr/local/src [cyg@master src]$ ./Cshell/hello.sh #相对路径 bash: ./Cshell/hello.sh: Permission denied [cyg@master src]$ /usr/local/src/Cshell/hello.sh #绝对路径 bash: /usr/local/src/Cshell/hello.sh: Permission denied [cyg@master src]$ cd Cshell [cyg@master Cshell]$ ./hello.sh #当前路径 bash: ./hello.sh: Permission denied

都提示Permission denied,没有执行权限,无法执行。
[cyg@master Cshell]$ ll #可看到该文件拥有者是root,只有它才有能力修改权限 -rw-r--r-- 1 root root67 May 31 16:08 hello.sh [cyg@master Cshell]$ su Password: [root@master Cshell]# chmod a+x hello.sh [root@master Cshell]# /usr/local/src/Cshell/hello.sh Hello World! [root@master Cshell]# ./hello.sh #这里的点 是指当前路径,不要点符号搞混了! Hello World! [root@master Cshell]# cd .. [root@master src]# ./Cshell/hello.sh Hello World!

1.4 实例
  • 当前shell执行的脚本文件(如:source 或. /script_file_name),对于脚本中的变量,变量不管是否有export,一旦执行脚本完毕,是还可以在当前shell中访问脚本中的变量的。
  • 子shell(子进程)中执行脚本(如:用相对或绝对路径/script_file_name[bash] [file_name.sh]执行),一旦执行完毕,随即返回父shell,脚本中的变量将无法访问。
实例
[root@master Cshell]# pwd /usr/local/src/Cshell [root@master Cshell]# vim w.sh h="hello" export w="world" echo $h [root@master Cshell]# chmod +x w.sh [root@master Cshell]# ./w.sh hello [root@master Cshell]# echo $h #不管有无export,都将无法再访问这两个变量了。[root@master Cshell]# echo $w[root@master Cshell]# . ./w.sh hello [root@master Cshell]# echo $h hello [root@master Cshell]# echo $w world

2、export关键字终出场 到目前为止,export还没真正登场,只有预热。前面的预热都是为了更好地掌握export
2.0 Linux中变量类型知多少 这里只说下个人理解的人为分类。
再次回到一句话:Linux是多用户多任务操作系统。在一个Linxu系统上,用户一般情况下一定有两类:
  • root
  • 普通用户,可能有多个以上。
为了更好地理解export,在此仅将变量分为两大类:
  • 1 环境变量
    • 系统级环境变量:每个登录到Linux系统的用户都能够读取到的环境变量;
    • 用户级环境变量:每个登录到Linux系统的用户只能够读取到属于自己的用户级的环境变量。这样用户登录后,将由自己专用的运行环境。
  • 2 非环境变量 一般是用户自定义,比如某个用户(包括root)自己写的某个程序中的自定义变量。
所以,为了方便管理和持久化,会将环境变量写入一些配置文件中。相应地也分成了系统级、用户级配置文件:
  • 系统级:/etc/profile/etc/bashrc、等,对其的修改将影响到每一个用户。
  • 用户级:~/.profile~/.bashrc等,对其的修改只对该用户有影响。
常见环境变量有PATHHOMEHISTSIZESHELLPS1LANGRANDOM等等。
在执行时,对于环境变量(包括不同级别的配置文件)、普通变量,是有顺序的。
环境变量、自定义变量两者之间可以相互转换:
Shell - 03 深刻理解export关键字(再也不会犯迷糊)
文章图片

Linux环境变量文件介绍
Shell变量之自定义变量、环境变量
2.1 export实践 import 译作:进口、输入、引进、导入
export 译作:出口、输出、调出、导出
在python、scala语言中,经常看到import关键字,但没有export关键字。而export出现在了shell这个脚本语言中。为什么?
个人理解,因为环境变量在Linux中大部分都存于配置文件中,要访问某个环境变量时,就需要将它导出、读出export)。从而Shell或Linux没有 import一说了。
实例:
[root@master Cshell]# t1="t1_no_export" [root@master Cshell]# export t2="t2_yes_export" [root@master Cshell]# vim test_export.sh #!/bin/bashecho "TERM = $TERM"export TERM echo "TERM = $TERM"echo "t1 = ${t1}" echo "t2 = ${t2}" [root@master Cshell]# sh test_export.sh TERM = linux TERM = linux t1 = t2 = t2_yes_export [root@master Cshell]#

小结:
  • 对于一个【非环境变量】,不加export在子shell中运行的脚本中取不到数据(如t1);而t2这个【非环境变量】加了export(在此变成了环境变量,不过未持久化),在子shell中运行的脚本中能取到它的数据。说明子进程(子shell)在继承父进程(父shell)时,“拷贝”了一份t2给自己(子shell也可以修改它,不过父shell看不到修改),而t1是父shell独有的。
  • 对于一个【环境变量】,放在一个脚本中,加不加export都无区别。
还可参考【菜鸟教程:Linux export命令】

    推荐阅读