目录
环境变量
常见的环境变量
基本概念
查看环境变量内容的方法
测试环境变量PATH
与环境变量相关的命令
Linux操作系统下C/C++程序代码中获取环境变量的方式
环境变量的组织方式
环境变量通常具有全局属性
环境变量
问题:
注意:可执行程序 等价于 命令/指令/工具/软件/程序/二进制文件、
由上可知,process 是一个可执行程序(64byte),因此,我们也可以称 process 为命令/指令/工具/软件/程序/二进制文件,其次,对于 ls 和 pwd 等等而言,它们可以被称为:命令/指令/工具/软件/程序/二进制文件,也可以被称为可执行程序(64byte)、
我们自己生成的可执行程序与系统(以 Linux 操作系统为例)指令之间没有区别,他们都是可执行程序,那么为什么系统的可执行程序可以直接运行,不需要添加路径,比如直接运行:ls,ll,whoami 等等,但是我们自己生成的可执行程序要想运行,就必须要加上路径呢?比如:./process,若运行自己生成的可执行程序但不添加路径时,则会出现以下问题:
原因:在 Linux 操作系统中存在相关的环境变量PATH,其次要知道,要想成功运行一个可执行程序(包括自己生成的可执行程序和系统的可执行程序),就必须要先找到它,当运行的可执行程序(包括自己生成的可执行程序和系统的可执行程序)不添加路径时,Linux 操作系统会默认在环境变量PATH中所存储的路径下去寻找该系统的或者我们自己生成的可执行程序,又因该环境变量PATH中存储了所有的系统可执行程序的所在路径,所以当运行系统的可执行程序但不添加路径时,Linux 操作系统根据环境变量PATH中的路径能够找到该系统的可执行程序,因此当运行系统的可执行程序但不添加路径时,能够成功运行,但是,环境变量PATH中并未保存自己生成的可执行程序的所在路径,因此,当运行自己生成的可执行程序但不添加路径时,Linux 操作系统根据环境变量PATH中的路径不能找到该自己生成的可执行程序,因此当运行自己生成的可执行程序但不添加路径时,不能成功运行,会报错:command not found 、
当我们运行一个可执行程序(包括系统的可执行程序和自己生成的可执行程序)但不添加路径时,Linux 操作系统默认会根据环境变量PATH中的内容(多个路径),从第一个路径中开始搜索是否其中存在该可执行程序(包括系统的可执行程序和自己生成的可执行程序),若能找到,则可以成功运行,此时搜索结束,若找不到,则在下一个路径中继续搜索,如果所有路径下都不存在该可执行程序(包括系统的可执行程序和自己生成的可执行程序),则会报错:command not found 、
当运行的可执行程序(包括自己生成的可执行程序和系统的可执行程序)主动添加路径时,则Linux 操作系统会根据该主动添加的路径去寻找该系统的或者我们自己生成的可执行程序,而不会再去环境变量PATH中所存储的路径下去寻找该系统的或者我们自己生成的可执行程序,若我们主动添加的路径下能够找到该自己生成的可执行程序和系统的可执行程序,则就能够成功运行,若找不到,则就会报错,不能成功运行、
拓展:
若想直接成功运行一个我们自己生成的可执行程序但不带路径,则有如下两种方法:
方法一:
可以将我们自己生成的可执行程序拷贝一份放到环境变量PATH中的任何一个路径下,其次,由于系统的可执行程序都存储于 /usr/bin 路径下,而环境变量PATH中又包含了该路径,因此,可将我们自己生成的可执行程序拷贝一份放到 /usr/bin 路径下,由此可知,我们能够在环境变量PATH中的 /usr/bin 路径下找到我们自己生成的可执行程序,故,当我们运行该自己生成的可执行程序但不带路径时,也是可以成功运行的,如下所示:
[HJM@hjmlcc ~]$ ls Makefile process process.c [HJM@hjmlcc ~]$ echo $PATH /usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/HJM/.local/bin:/home/HJM/bin [HJM@hjmlcc ~]$ ls /usr/bin/process ls: cannot access /usr/bin/process: No such file or directory [HJM@hjmlcc ~]$ su - Password: Last login: Wed Jan 4 13:22:37 CST 2023 on pts/1 Last failed login: Fri Jan 6 13:25:21 CST 2023 from 175.23.169.25 on ssh:notty There were 3623 failed login attempts since the last successful login. [root@hjmlcc ~]# cp process /usr/bin cp: cannot stat ‘process’: No such file or directory [root@hjmlcc ~]# cd /home/HJM [root@hjmlcc HJM]# ls Makefile process process.c [root@hjmlcc HJM]# cp process /usr/bin //注意:此处必须要切换到root用户再进行拷贝,或者可以通过sudo cp process /usr/bin指令来完成 //拷贝、 [root@hjmlcc HJM]# su HJM [HJM@hjmlcc ~]$ pwd /home/HJM [HJM@hjmlcc ~]$ ls Makefile process process.c [HJM@hjmlcc ~]$ ls /usr/bin/process /usr/bin/process [HJM@hjmlcc ~]$ process //此时直接运行我们自己生成的可执行程序且不带路径,则可以成功运行、 Hello,Lcc! Hello,Lcc! ^C [HJM@hjmlcc ~]$
注意:最好不要将自己生成的可执行程序拷贝放到环境变量PATH中的某些路径下,因为可能会造成指令(工具/可执行程序等等)污染,比如:我们如上所示的自己生成的可执行程序放到了环境变量PATH中的路径 /usr/bin 下,如果我们更改了普通文件process.c中的内容,并重新生成了一个名字也为process的自己生成的可执行程序,此时,该名称为process的自己生成的可执行程序与之前放入到环境变量PATH中的路径 /usr/bin 下的名称为process的自己生成的可执行程序并不是同一个自己生成的可执行程序,则有可能会造成混淆,所以最好不要使用这种方法,通过下面的操作将刚刚拷贝到环境变量PATH中的路径 /usr/bin 下的自己生成的可执行程序删除掉:
[HJM@hjmlcc ~]$ ls Makefile process process.c [HJM@hjmlcc ~]$ process Hello,Lcc! Hello,Lcc! ^C [HJM@hjmlcc ~]$ su //su -也是可以的、 Password: [root@hjmlcc HJM]# ls Makefile process process.c [root@hjmlcc HJM]# rm /usr/bin/process rm: remove regular file ‘/usr/bin/process’? y [root@hjmlcc HJM]# ls /usr/bin/process ls: cannot access /usr/bin/process: No such file or directory [root@hjmlcc HJM]# su HJM [HJM@hjmlcc ~]$ process bash: process: command not found [HJM@hjmlcc ~]$
也可以通过指令:sudo rm /usr/bin/process 进行删除、
方法二:
预备知识:在 Linux 命令行以及系统中,也可以定义变量(命令行变量),通常可以定义两种变量(命令行变量),如下所示:
//一: //1、 [HJM@hjmlcc ~]$ ls Makefile process process.c [HJM@hjmlcc ~]$ aaaaa=100 //如此定义的变量为本地(普通)变量、 [HJM@hjmlcc ~]$ echo $aaaaa 100 [HJM@hjmlcc ~]$ env | grep aaaaa //由于这不是环境变量,所以查不到、 [HJM@hjmlcc ~]$ //2、 [HJM@hjmlcc ~]$ export bbbbb=123 //如此定义的变量为环境变量(具有全局属性),在定义本地(普通)变量的同时将其导出,则该本地(普通)变 //量就会变成环境变量,也可以理解为通过这种方式直接定义了环境变量、 [HJM@hjmlcc ~]$ env | grep bbbbb bbbbb=123 [HJM@hjmlcc ~]$ echo $bbbbb 123 [HJM@hjmlcc ~]$ //注意: //echo $aaaaa可以将普通(本地)变量的内容打印在屏幕上,也可以将环境变量的内容打印在屏幕上, //如:echo $bbbbb,其次,此处的本地(普通)变量和环境变量均具有临时性,当关闭Xshell且重新登陆用户时 //,之前设置的所有的环境变量和所有的本地(普通)变量均会消失不见、 //二: //删除变量: //删除本地(普通)变量或删除环境变量: //1、删除环境变量、 [HJM@hjmlcc ~]$ pwd /home/HJM [HJM@hjmlcc ~]$ export bbbbb=1234 [HJM@hjmlcc ~]$ echo $bbbbb 1234 [HJM@hjmlcc ~]$ env | grep bbbbb bbbbb=1234 [HJM@hjmlcc ~]$ unset bbbbb [HJM@hjmlcc ~]$ echo $bbbbb [HJM@hjmlcc ~]$ env | grep bbbbb [HJM@hjmlcc ~]$ //2、删除普通(本地)变量、 [HJM@hjmlcc ~]$ pwd /home/HJM [HJM@hjmlcc ~]$ aaaaa=1000 [HJM@hjmlcc ~]$ echo $aaaaa 1000 [HJM@hjmlcc ~]$ env | grep aaaaa [HJM@hjmlcc ~]$ unset aaaaa [HJM@hjmlcc ~]$ echo $aaaaa [HJM@hjmlcc ~]$ env | grep aaaaa [HJM@hjmlcc ~]$
注意:
[HJM@hjmlcc ~]$ ls Makefile process process.c [HJM@hjmlcc ~]$ echo $PATH //将环境变量PATH的内容打印在屏幕上、 /usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/HJM/.local/bin:/home/HJM/bin [HJM@hjmlcc ~]$ pwd /home/HJM [HJM@hjmlcc ~]$ export PATH=/home/HJM //该操作会把环境变量PATH的内容更改为:/home/HJM [HJM@hjmlcc ~]$ echo $PATH /home/HJM [HJM@hjmlcc ~]$ ls -bash: ls: command not found [HJM@hjmlcc ~]$ touch -bash: touch: command not found [HJM@hjmlcc ~]$ ll -bash: ls: command not found [HJM@hjmlcc ~]$ //由于在 Linux 命令行上设置(修改)的环境变量的内容具有临时性,当关闭Xshell,再次重启登录用户时,会 //发现环境变量PATH中的内容又会恢复 //到:/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/HJM/.local/bin:/home/HJM/bin //,这是因为,Linux 操作系统中的环境变量存在于内存中,且不是以文件的形式存在,当再次重启Xshell,登 //录用户时,会重新读取系统中的配置文件,重新生成环境变量,若想要改变原来的环境变量中的内容且保持永 //久有效,即重启Xshell再登录用户时也会保持修改后的内容,则需要更改配置文件,配置文件在系统中,与云 //服务器无关,上述修改环境变量中的内容只是在内存层面进行的修改,不会修改到系统中相关的配置文件,在 //系统中关于每次重启Xshell再登录用户时相关的配置文件一般可以在.bash profile和.bashrc两个普通 //文件中进行设置,了解即可、
可以将自己生成的可执行程序所在的路径直接添加到环境变量PATH中,这样也可以解决问题,如下所示:
[HJM@hjmlcc ~]$ ls Makefile process process.c [HJM@hjmlcc ~]$ echo $PATH /usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/HJM/.local/bin:/home/HJM/bin [HJM@hjmlcc ~]$ process -bash: process: command not found [HJM@hjmlcc ~]$ pwd /home/HJM [HJM@hjmlcc ~]$ export PATH=$PATH:/home/HJM //$PATH即拿到了原来的环境变量PATH中的所有内容,再使用:将其与/home/HJM分隔开来,整体再赋值给新 //的环境变量PATH,此时再查看新的环境变量PATH的内容就会出现下面这样的情况,要知道,此处 //的$PATH:/home/HJM等价于/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin //:/home/HJM/.local/bin:/home/HJM/bin:/home/HJM、 [HJM@hjmlcc ~]$ echo $PATH /usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/HJM/.local/bin:/home/HJM/bin:/home/HJM [HJM@hjmlcc ~]$ process Hello,L! Hello,L! Hello,L! ^C [HJM@hjmlcc ~]$ vim process.c [HJM@hjmlcc ~]$ make gcc process.c -o process [HJM@hjmlcc ~]$ ls Makefile process process.c [HJM@hjmlcc ~]$ process Hello,Lcc! Hello,Lcc! ^C [HJM@hjmlcc ~]$
[HJM@hjmlcc ~]$ echo $PATH /usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/HJM/.local/bin:/home/HJM/bin [HJM@hjmlcc ~]$ which ls alias ls='ls --color=auto' /usr/bin/ls [HJM@hjmlcc ~]$ which touch /usr/bin/touch [HJM@hjmlcc ~]$ which process /usr/bin/which: no process in (/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/HJM/.local/bin:/home/HJM/bin) [HJM@hjmlcc ~]$ export PATH=$PATH:/home/HJM [HJM@hjmlcc ~]$ echo $PATH /usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/HJM/.local/bin:/home/HJM/bin:/home/HJM [HJM@hjmlcc ~]$ which process ~/process [HJM@hjmlcc ~]$
注意:which 指令(工具/可执行程序等)是根据环境变量PATH中的所有路径去查找可执行程序(包括自己生成的可执行程序和系统的可执行程序)的、
在 Linux 操作系统中,存在相关的环境变量PATH,该环境变量PATH中默认保存了所有系统可执行程序的搜索路径,环境变量是在开机或者登录用户时,在 Linux 操作系统中自动形成的一组变量,只要是变量,就一定存在变量名和变量内容,不同的环境变量拥有不同的应用场景,在Linux 操作系统中,存在相关的环境变量,查看环境变量的方式如下所示:
[HJM@hjmlcc ~]$ ls
Makefile process process.c
[HJM@hjmlcc ~]$ env //查看 Linux 操作系统中的环境变量、
XDG_SESSION_ID=571347
HOSTNAME=hjmlcc
TERM=xterm
SHELL=/bin/bash
HISTSIZE=3000
SSH_CLIENT=111.33.240.197 14986 22
SSH_TTY=/dev/pts/0
USER=HJM
LD_LIBRARY_PATH=:/home/HJM/.VimForCpp/vim/bundle/YCM.so/el7.x86_64
LS_COLORS=rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:mi=01;05;37;41:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arc=01;31:*.arj=01;31:*.taz=01;31:*.lha=01;31:*.lz4=01;31:*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;31:*.tzo=01;31:*.t7z=01;31:*.zip=01;31:*.z=01;31:*.Z=01;31:*.dz=01;31:*.gz=01;31:*.lrz=01;31:*.lz=01;31:*.lzo=01;31:*.xz=01;31:*.bz2=01;31:*.bz=01;31:*.tbz=01;31:*.tbz2=01;31:*.tz=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.war=01;31:*.ear=01;31:*.sar=01;31:*.rar=01;31:*.alz=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.cab=01;31:*.jpg=01;35:*.jpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.svg=01;35:*.svgz=01;35:*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.webm=01;35:*.ogm=01;35:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.flv=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv=01;35:*.cgm=01;35:*.emf=01;35:*.axv=01;35:*.anx=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=01;36:*.au=01;36:*.flac=01;36:*.mid=01;36:*.midi=01;36:*.mka=01;36:*.mp3=01;36:*.mpc=01;36:*.ogg=01;36:*.ra=01;36:*.wav=01;36:*.axa=01;36:*.oga=01;36:*.spx=01;36:*.xspf=01;36:
MAIL=/var/spool/mail/HJM
PATH=/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/HJM/.local/bin:/home/HJM/bin
PWD=/home/HJM
LANG=en_US.utf8
SHLVL=1
HOME=/home/HJM
LOGNAME=HJM
SSH_CONNECTION=111.33.240.197 14986 10.0.8.8 22
LESSOPEN=||/usr/bin/lesspipe.sh %s
PROMPT_COMMAND=history -a; printf "\033]0;%s@%s:%s\007" "${USER}" "${HOSTNAME%%.*}" "${PWD/#$HOME/~}"
XDG_RUNTIME_DIR=/run/user/1002
HISTTIMEFORMAT=%F %T
_=/usr/bin/env
[HJM@hjmlcc ~]$
[HJM@hjmlcc ~]$ ls
Makefile process process.c
[HJM@hjmlcc ~]$ env | grep 'PATH'
LD_LIBRARY_PATH=:/home/HJM/.VimForCpp/vim/bundle/YCM.so/el7.x86_64
PATH=/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/HJM/.local/bin:/home/HJM/bin
[HJM@hjmlcc ~]$ env | grep PATH
LD_LIBRARY_PATH=:/home/HJM/.VimForCpp/vim/bundle/YCM.so/el7.x86_64
PATH=/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/HJM/.local/bin:/home/HJM/bin
[HJM@hjmlcc ~]$
//两种写法得到一样的结果、
打印出环境变量中的内容:
[HJM@hjmlcc ~]$ echo "aaaaa" //将字符串"aaaaa"打印在屏幕上、
aaaaa
[HJM@hjmlcc ~]$ echo PATH //此时PATH会被认作是字符串"PATH",并打印在屏幕上、
PATH
[HJM@hjmlcc ~]$ echo $PATH //将环境变量PATH的内容打印在屏幕上、
/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/HJM/.local/bin:/home/HJM/bin
[HJM@hjmlcc ~]$ //:作为分隔符,分隔多个路径、
注意:以上均是以 Linux 操作系统为例,但还要知道,在 Windows 操作系统中也存在相关的环境变量,此电脑—属性—高级系统设置—环境变量、
拓展:
当运行系统的可执行程序时,我们也可以为其添加路径,如下所示:
[HJM@hjmlcc ~]$ ls Makefile process process.c [HJM@hjmlcc ~]$ /usr/bin/ls Makefile process process.c [HJM@hjmlcc ~]$ /usr/bin/pwd /home/HJM [HJM@hjmlcc ~]$
常见的环境变量
[HJM@hjmlcc ~]$ env //查看Linux操作系统下所有的环境变量、
XDG_SESSION_ID=582015
HOSTNAME=hjmlcc
TERM=xterm
SHELL=/bin/bash
HISTSIZE=3000
SSH_CLIENT=111.30.235.195 14463 22
...
...
LESSOPEN=||/usr/bin/lesspipe.sh %s
PROMPT_COMMAND=history -a; printf "\033]0;%s@%s:%s\007" "${USER}" "${HOSTNAME%%.*}" "${PWD/#$HOME/~}"
XDG_RUNTIME_DIR=/run/user/1002
HISTTIMEFORMAT=%F %T
_=/usr/bin/env
[HJM@hjmlcc ~]$
MANPATH:代表man手册的搜索路径、
HOSTNAME:代表当前使用的云服务器的机器名、
HISTSIZE:在 Linux 命令行中,可以使用上下键调出历史使用过的指令,该环境变量代表最多记录的历史指令条数,使用指令 history 便可查看记录下来的历史指令,还要知道,查询出来的历史指令的编号并不一定是从1开始的,可以使用指令 history | wc -l 来统计所记录下来的历史指令的条数、
PWD:代表当前使用的用户所处的路径(指定当前用户的主工作目录,即用户登陆到Linux操作系统中时,默认的目录)、
HOME:代表当前用户的家目录、
PATH:指定命令(可执行程序等等)的搜索路径、
SHELL: 当前Shell,它的值通常是/bin/bash、
环境变量都写在配置文件中,当每一次打开Xshell,登录用户时,Linux操作系统都会自动执行相关的配置文件,从而导出环境变量,我们在Linux命令行中修改环境变量的内容时,不会改变配置文件中环境变量里的内容,所以我们每次打开Xshell,登录用户时,Linux操作系统会再次自动执行相关的配置文件,因此,我们在Linux命令行中修改的环境变量的内容具有临时性,当每一次打开Xshell,登录用户时,环境变量中的内容总保持与配置文件中的环境变量里的内容一致、
拓展:
[HJM@hjmlcc ~]$ pwd
/home/HJM
[HJM@hjmlcc ~]$ ls
Makefile process process.c
[HJM@hjmlcc ~]$ cd ..
[HJM@hjmlcc home]$ pwd
/home
[HJM@hjmlcc home]$ cd - // - 代表的是家目录,此处cd -代表的就是进入普通用户HJM的家目录下、
/home/HJM
[HJM@hjmlcc ~]$
基本概念
1、环境变量(environment variables)一般是指在操作系统中用来指定操作系统运行环境的一些参数,如:我们在编写C/C++代码的时候,在链接的时候,从来不知道我们的所链接的动态静态库在哪里,但是照样可以链接成功,生成可执行程序,原因就是有相关环境变量帮助编译器进行查找、2、环境变量通常具有某些特殊用途,还有在系统当中通常具有全局特性、
查看环境变量内容的方法
echo $NAME //NAME:环境变量的名称、
测试环境变量PATH
测试环境变量 PATH 请见上述所示、
拓展:
//1、 [HJM@hjmlcc ~]$ pwd /home/HJM [HJM@hjmlcc ~]$ aaa=123 //本地(普通)变量、 [HJM@hjmlcc ~]$ echo $aaa 123 [HJM@hjmlcc ~]$ env | grep aaa [HJM@hjmlcc ~]$ export aaa=321 //环境变量、 [HJM@hjmlcc ~]$ echo $aaa 321 [HJM@hjmlcc ~]$ env | grep aaa aaa=321 //在定义本地(普通)变量的同时将其导出,则该本地(普通)变量就会变成环境变量,也可以理解为通过这种 //方式直接定义了环境变量、 [HJM@hjmlcc ~]$ //注意:此处的变量aaa由本地(普通)变量变成了环境变量、 //2、 [HJM@hjmlcc ~]$ pwd /home/HJM [HJM@hjmlcc ~]$ aaa=123 //本地(普通)变量、 [HJM@hjmlcc ~]$ echo $aaa 123 [HJM@hjmlcc ~]$ env | grep aaa [HJM@hjmlcc ~]$ export aaa=123 //环境变量、 [HJM@hjmlcc ~]$ echo $aaa 123 [HJM@hjmlcc ~]$ env | grep aaa aaa=123 //将定义好的本地(普通)变量导出,该本地(普通)变量就会变成环境变量,也可以理解为通过这种方式直接 //定义了环境变量、 //注意:此处的变量aaa由本地(普通)变量变成了环境变量、 //3、 [HJM@hjmlcc ~]$ export zzz=100 //环境变量、 [HJM@hjmlcc ~]$ echo $zzz 100 [HJM@hjmlcc ~]$ env | grep zzz zzz=100 [HJM@hjmlcc ~]$ zzz=123 //仍是环境变量、 [HJM@hjmlcc ~]$ echo $zzz 123 [HJM@hjmlcc ~]$ env | grep zzz zzz=123 [HJM@hjmlcc ~]$
[HJM@hjmlcc ~]$ qwe=100 //本地(普通)变量、 [HJM@hjmlcc ~]$ echo $qwe 100 [HJM@hjmlcc ~]$ env | grep qwe [HJM@hjmlcc ~]$ set | grep qwe //可以查看本地(普通)变量、 qwe=100 [HJM@hjmlcc ~]$ export ewq=200 //环境变量、 [HJM@hjmlcc ~]$ echo $ewq 200 [HJM@hjmlcc ~]$ env | grep ewq ewq=200 [HJM@hjmlcc ~]$ set | grep ewq //也可以查看环境变量、 ewq=200 [HJM@hjmlcc ~]$
与环境变量相关的命令
- 1、echo:显示某个环境变量的内容、
- 2、export:设置一个新的环境变量、
- 3、env:显示所有的环境变量、
- 4、unset:清除环境变量或普通(本地)变量、
- 5、set:显示本地定义的shell变量(普通变量/本地变量)和环境变量、
Linux操作系统下C/C++程序代码中获取环境变量的方式
Linux操作系统中,在Linux命令行中可以直接使用Linux操作系统拥有的环境变量,而在Linux系统下自己的C/C++程序中没有办法直接使用Linux操作系统拥有的环境变量,需要先获取,再使用,那么如何获取呢?
预备知识:
main函数可以不带参数(形参),也可以带参数,最多可以带三个参数、
int main(int argc, char* argv[], char* envp[]) { return 0; } //char* argv[]:字符指针数组、 //int argc:该字符指针数组char* argv[]的元素个数为argc+1个、 //char* envp[]:字符指针数组、
我们先谈一下它的前两个参数,先来一段代码作为测试用例:
如上图所示,argc即为4,我们给main函数传递的形参:int argc 和 char* argv[ ] 称为(Linux)命令行参数,传递的内容本质上就是在Linux命令行中输入的可执行程序的程序名(或是./+可执行程序的程序名)和选项,并且该字符指针数组以空指针NULL结尾,该字符指针数组的元素个数总是要比在Linux命令行中输入的常量字符串的个数多1个、
main函数所接收到的前两个形参的来源,具体在后期进程控制时再进行阐述、
main函数的前两个形参存在的意义是什么?
通过下面这个例子来回答这个问题:
实现一个Linux命令行版的计算器,通过在命令行中输入:./process -a 10 20 ,即可自动完成两个 int 类型的整数的相加,通过在命令行中输入:./process -s 10 20 ,即可自动完成两个 int 类型的整数的相减,其他功能具体见如下代码所示:
[HJM@hjmlcc ~]$ ls Makefile mycal.c [HJM@hjmlcc ~]$ cat mycal.c #include<stdio.h> #include<string.h> #include<stdlib.h> int main(int argc,char* argv[]) { //char* argv[]:字符指针数组,该数组有argc+1个元素、 if(argc!=4) { printf("Usage:%s [-a|-s|-m|-d] one_data two_data\n",argv[0]); return 0; } int x=atoi(argv[2]); //将字符指针指向的内容转换为整数、 int y=atoi(argv[3]); if(strcmp("-a",argv[1])==0) { //加法 printf("%d+%d=%d\n",x,y,x+y); } else if(strcmp("-s",argv[1])==0) { //减法 printf("%d-%d=%d\n",x,y,x-y); } else if(strcmp("-m",argv[1])==0) { //乘法 printf("%d*%d=%d\n",x,y,x*y); } else if(strcmp("-d",argv[1])==0 && y!=0) { //除法 printf("%d/%d=%d\n",x,y,x/y); } else{ printf("Usage:%s [-a|-s|-m|-d] one_data two_data\n",argv[0]); } return 0; } [HJM@hjmlcc ~]$ make gcc mycal.c -o mycal [HJM@hjmlcc ~]$ ./mycal -a 10 20 10+20=30 [HJM@hjmlcc ~]$ ./mycal -s 10 20 10-20=-10 [HJM@hjmlcc ~]$ ./mycal -m 10 20 10*20=200 [HJM@hjmlcc ~]$ ./mycal -d 10 20 10/20=0 [HJM@hjmlcc ~]$ ./mycal Usage:./mycal [-a|-s|-m|-d] one_data two_data [HJM@hjmlcc ~]$ ./mycal -d 10 0 Usage:./mycal [-a|-s|-m|-d] one_data two_data [HJM@hjmlcc ~]$
意义:
同一份程序代码,通过接收不同的参数(形参),让同一份程序代码有不同的执行逻辑,从而得到不同的执行结果,在Linux命令行中,Linux操作系统会根据不同的选项,让不同的命令(可执行程序等等),可以有不同的表现,这就是指令(命令等等)中各个选项的由来和起作用的方式!!这也就是Linux命令行参数存在的意义,同样也就是main函数中前两个参数存在的意义,在Windows系统下,也是如此、
拓展:
如上图所示,可以完成批量替换,将所有的 process 一次性全部替换成 mycal 、
实际上,main函数可以带第三个参数(形参),下面我们来具体谈一下:
// C或C++程序代码中获取环境变量的方式:
//方案一:
[HJM@hjmlcc ~]$ ls
Makefile mycal.c process.c
[HJM@hjmlcc ~]$ cat process.c
#include<stdio.h>
int main(int argc,char* argv[],char* env[])
{
//char* env[]:字符指针数组,该字符指针数组的最后一个元素也指向空指针NULL、
int i=0;
for(i=0;env[i];i++)
{
printf("env[%d]:%s\n",i,env[i]);
}
return 0;
}
[HJM@hjmlcc ~]$ gcc process.c
[HJM@hjmlcc ~]$ ls
a.out Makefile mycal.c process.c
[HJM@hjmlcc ~]$ ./a.out
env[0]:XDG_SESSION_ID=586394
env[1]:HOSTNAME=hjmlcc
env[2]:TERM=xterm
env[3]:SHELL=/bin/bash
env[4]:HISTSIZE=3000
env[5]:SSH_CLIENT=111.30.235.195 14443 22
env[6]:SSH_TTY=/dev/pts/1
env[7]:USER=HJM
env[8]:LD_LIBRARY_PATH=:/home/HJM/.VimForCpp/vim/bundle/YCM.so/el7.x86_64
env[9]:LS_COLORS=rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:mi=01;05;37;41:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arc=01;31:*.arj=01;31:*.taz=01;31:*.lha=01;31:*.lz4=01;31:*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;31:*.tzo=01;31:*.t7z=01;31:*.zip=01;31:*.z=01;31:*.Z=01;31:*.dz=01;31:*.gz=01;31:*.lrz=01;31:*.lz=01;31:*.lzo=01;31:*.xz=01;31:*.bz2=01;31:*.bz=01;31:*.tbz=01;31:*.tbz2=01;31:*.tz=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.war=01;31:*.ear=01;31:*.sar=01;31:*.rar=01;31:*.alz=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.cab=01;31:*.jpg=01;35:*.jpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.svg=01;35:*.svgz=01;35:*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.webm=01;35:*.ogm=01;35:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.flv=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv=01;35:*.cgm=01;35:*.emf=01;35:*.axv=01;35:*.anx=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=01;36:*.au=01;36:*.flac=01;36:*.mid=01;36:*.midi=01;36:*.mka=01;36:*.mp3=01;36:*.mpc=01;36:*.ogg=01;36:*.ra=01;36:*.wav=01;36:*.axa=01;36:*.oga=01;36:*.spx=01;36:*.xspf=01;36:
env[10]:MAIL=/var/spool/mail/HJM
env[11]:PATH=/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/HJM/.local/bin:/home/HJM/bin
env[12]:PWD=/home/HJM
env[13]:LANG=en_US.utf8
env[14]:SHLVL=1
env[15]:HOME=/home/HJM
env[16]:LOGNAME=HJM
env[17]:SSH_CONNECTION=111.30.235.195 14443 10.0.8.8 22
env[18]:LESSOPEN=||/usr/bin/lesspipe.sh %s
env[19]:PROMPT_COMMAND=history -a; printf "\033]0;%s@%s:%s\007" "${USER}" "${HOSTNAME%%.*}" "${PWD/#$HOME/~}"
env[20]:XDG_RUNTIME_DIR=/run/user/1002
env[21]:HISTTIMEFORMAT=%F %T
env[22]:_=./a.out
[HJM@hjmlcc ~]$ //注意: 字符指针数组char* env[]的元素个数与int整型变量argc无关、
//方案二:
//C语言自动定义一个第三方变量(指针变量)environ,属于全局变量、
//1、
[HJM@hjmlcc ~]$ man environ
EXEC(3P) POSIX Programmer's Manual EXEC(3P)
PROLOG
This manual page is part of the POSIX Programmer's Manual. The Linux implementation of this interface may differ (con‐
sult the corresponding Linux manual page for details of Linux behavior), or the interface may not be implemented on
Linux.
NAME
environ, execl, execv, execle, execve, execlp, execvp - execute a file
SYNOPSIS
#include <unistd.h>
extern char **environ;
int execl(const char *path, const char *arg0, ... /*, (char *)0 */);
int execv(const char *path, char *const argv[]);
int execle(const char *path, const char *arg0, ... /*,
(char *)0, char *const envp[]*/);
int execve(const char *path, char *const argv[], char *const envp[]);
int execlp(const char *file, const char *arg0, ... /*, (char *)0 */);
int execvp(const char *file, char *const argv[]);
DESCRIPTION
The exec family of functions shall replace the current process image with a new process image. The new image shall be
constructed from a regular, executable file called the new process image file. There shall be no return from a success‐
ful exec, because the calling process image is overlaid by the new process image.
When a C-language program is executed as a result of this call, it shall be entered as a C-language function call as
follows:
Manual page environ(3p) line 1 (press h for help or q to quit)
//2、
[HJM@hjmlcc ~]$ ls
Makefile mycal.c process.c
[HJM@hjmlcc ~]$ cat process.c
#include<stdio.h>
#include<unistd.h>
int main()
{
extern char** environ; //仅仅是声明,不是程序员自己定义的,而是C语言自动定义的、
int i=0;
for(i=0;environ[i];i++)
{
printf("environ[%d]:%s\n",i,environ[i]);
}
return 0;
}
[HJM@hjmlcc ~]$ gcc process.c
[HJM@hjmlcc ~]$ ls
a.out Makefile mycal.c process.c
[HJM@hjmlcc ~]$ ./a.out
environ[0]:XDG_SESSION_ID=586394
environ[1]:HOSTNAME=hjmlcc
environ[2]:TERM=xterm
environ[3]:SHELL=/bin/bash
environ[4]:HISTSIZE=3000
environ[5]:SSH_CLIENT=111.30.235.195 14443 22
environ[6]:SSH_TTY=/dev/pts/1
environ[7]:USER=HJM
environ[8]:LD_LIBRARY_PATH=:/home/HJM/.VimForCpp/vim/bundle/YCM.so/el7.x86_64
environ[9]:LS_COLORS=rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:mi=01;05;37;41:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arc=01;31:*.arj=01;31:*.taz=01;31:*.lha=01;31:*.lz4=01;31:*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;31:*.tzo=01;31:*.t7z=01;31:*.zip=01;31:*.z=01;31:*.Z=01;31:*.dz=01;31:*.gz=01;31:*.lrz=01;31:*.lz=01;31:*.lzo=01;31:*.xz=01;31:*.bz2=01;31:*.bz=01;31:*.tbz=01;31:*.tbz2=01;31:*.tz=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.war=01;31:*.ear=01;31:*.sar=01;31:*.rar=01;31:*.alz=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.cab=01;31:*.jpg=01;35:*.jpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.svg=01;35:*.svgz=01;35:*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.webm=01;35:*.ogm=01;35:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.flv=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv=01;35:*.cgm=01;35:*.emf=01;35:*.axv=01;35:*.anx=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=01;36:*.au=01;36:*.flac=01;36:*.mid=01;36:*.midi=01;36:*.mka=01;36:*.mp3=01;36:*.mpc=01;36:*.ogg=01;36:*.ra=01;36:*.wav=01;36:*.axa=01;36:*.oga=01;36:*.spx=01;36:*.xspf=01;36:
environ[10]:MAIL=/var/spool/mail/HJM
environ[11]:PATH=/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/HJM/.local/bin:/home/HJM/bin
environ[12]:PWD=/home/HJM
environ[13]:LANG=en_US.utf8
environ[14]:SHLVL=1
environ[15]:HOME=/home/HJM
environ[16]:LOGNAME=HJM
environ[17]:SSH_CONNECTION=111.30.235.195 14443 10.0.8.8 22
environ[18]:LESSOPEN=||/usr/bin/lesspipe.sh %s
environ[19]:PROMPT_COMMAND=history -a; printf "\033]0;%s@%s:%s\007" "${USER}" "${HOSTNAME%%.*}" "${PWD/#$HOME/~}"
environ[20]:XDG_RUNTIME_DIR=/run/user/1002
environ[21]:HISTTIMEFORMAT=%F %T
environ[22]:_=./a.out
[HJM@hjmlcc ~]$ //注意:用此方法所打印出来的环境变量的个数与int整型变量argc无关、
//方案三:
//1、
[HJM@hjmlcc ~]$ man 3 getenv
GETENV(3) Linux Programmer's Manual GETENV(3)
NAME
getenv, secure_getenv - get an environment variable
SYNOPSIS
#include <stdlib.h>
char *getenv(const char *name);
char *secure_getenv(const char *name);
Feature Test Macro Requirements for glibc (see feature_test_macros(7)):
secure_getenv(): _GNU_SOURCE
DESCRIPTION
The getenv() function searches the environment list to find the environment variable name, and returns a pointer to the
corresponding value string.
The GNU-specific secure_getenv() function is just like getenv() except that it returns NULL in cases where "secure exe‐
cution" is required. Secure execution is required if one of the following conditions was true when the program run by
the calling process was loaded:
* the process's effective user ID did not match its real user ID or the process's effective group ID did not match its
real group ID (typically this is the result of executing a set-user-ID or set-group-ID program);
* the effective capability bit was set on the executable file; or
* the process has a nonempty permitted capability set.
Manual page getenv(3) line 1 (press h for help or q to quit)
//2、
[HJM@hjmlcc ~]$ ls
Makefile mycal.c process.c
[HJM@hjmlcc ~]$ cat process.c
#include<stdio.h>
#include<stdlib.h>
int main() //通过putenv在C或C++程序代码中设置环境变量,在后期再进行阐述、
{
char* val=getenv("PATH"); //返回环境变量PATH中的内容(常量字符串)首元素的地址、
printf("%s\n",val);
return 0; //常用getenv和putenv函数来访问特定的环境变量、
}
[HJM@hjmlcc ~]$ gcc process.c
[HJM@hjmlcc ~]$ ls
a.out Makefile mycal.c process.c
[HJM@hjmlcc ~]$ ./a.out
/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/HJM/.local/bin:/home/HJM/bin
[HJM@hjmlcc ~]$
main函数所接收到的第三个形参(char* env[ ])的来源,具体也在后期进程控制时再进行阐述、
总结:一个进程是会被传入环境变量参数的(char* env[ ])、
拓展1:
[HJM@hjmlcc ~]$ ls Makefile mycal.c process.c [HJM@hjmlcc ~]$ cat process.c #include<stdio.h> void fun() { //此处即使没有任何形参,但在该调用函数内部照样可以通过某种方式读取到传递过来的实参:10和20.5,但是过程比较麻烦,了解一下即可、 printf("Hello,Lcc\n"); } int main() { fun(10,20.5); //当某一个调用函数的声明和定义都没有任何形参时,如上所示: //此时传实参也能够成功编译的,此处的两个实参也会压栈,也会形成临时变量等等,只不过是在调用函数 //fun内部没有接收而已,在Linux操作系统下编译时不会报错,但是在Windows系统下的Vs编译器下进行编 //译时会报错,这就取决于编译器了,但是如果写成void fun(void)的话,则在Linux下编译时也会报错, //在Windows下的Vs编译器下进行编译时也会报错,此时在main函数中只能写成fun()才可以,我们之前 //写main函数时一般都不带任何形参,但要知道,本质上是已经给main函数传递了三个参数,只不过是main //函数并没有接收而已,在Windows下的Vs编译器下编译时,main函数不带任何形参是不会报错的,这一点与 //该例子不同,要注意、 return 0; } [HJM@hjmlcc ~]$ gcc process.c [HJM@hjmlcc ~]$ ls a.out Makefile mycal.c process.c [HJM@hjmlcc ~]$ ./a.out Hello,Lcc [HJM@hjmlcc ~]$
拓展2:
预备知识:
首先要明白,环境变量USER的值并不能简单的取决于当前用户,即:环境变量USER的值并不是简单的取决于通过指令whoami得到的值(当前用户),而是,环境变量USER的值代表的是当前登录用户,若当前登录用户发生了改变,那么环境变量USER的值就会随之发生改变,而对于指令 su 而言,只是改变了当前用户(将当前用户改变为root用户),但是并未改变当前登录用户,所以环境变量USER的值不会发生改变,而对于 su - 指令而言,不仅会改变当前用户(将当前用户改变为root用户),还会改变当前登录用户,因此环境变量USER的值也会随之改变,但是要注意,指令su 普通用户,此时不仅会改变当前用户,还会改变当前登录用户,因此环境变量USER的值也会随之改变、
总结:
通过指令whoami得到的结果只是当前用户,而环境变量USER代表的是当前登录用户,
在大部分情况下,当前用户与当前登录用户是一致的,但在部分情况下,指令whoami得到的结果和环境变量USER的值是不一样的(比如普通用户HJM仅仅通过 su 指令,将当前用户切换到了root用户,但是当前登录用户仍是普通用户HJM,因此环境变量USER的值仍是普通用户HJM,而不是root用户),所以我们不能简单的把指令whoami得到的结果(当前用户)直接看做环境变量USER的值、
环境变量是用来定义操作系统环境的,因此如果操作系统环境没有随用户身份(当前用户)切换的话,很多命令都无法正确执行,su 指令只是切换了当前用户(将当前用户改变为root用户),但是Shell环境并未发生改变,而指令 su - 不仅切换了当前用户(将当前用户改变为root用户),也切换了Shell环境,只用 su 指令不会读取目标用户的环境配置文件,但 su - 指令则会读取,所以我们之后再切换当前用户时,最好都带上 - ,避免出现不必要的错误,su 和 su - 指令在Linux和AIX操作系统下存在一定的区别,在AIX操作系统中一定要加上 - 、
具体请见博客:
su 和su -的区别_水墨胭脂的博客-CSDN博客_su和su -参考文章:(总结)Linux下su与su -命令的本质区别 我一直是习惯使用su username来切换用户的,而且并不知道su和su -有什么区别,一直以为是没啥区别的,但是在一个测试中,在测试的同事的指导下,他说你su - ××× 之后×××,那一刻突然福至心灵觉得可能二者是有区别的,然后执行了su - 用户 之后,果然得到了su 用户的时候得不到的结果。。。。。然后还发现,以前执行dbhttps://blog.csdn.net/nayanminxing/article/details/76424115?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522167335462916800182133259%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=167335462916800182133259&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduend~default-2-76424115-null-null.142%5Ev70%5Eone_line,201%5Ev4%5Eadd_ask&utm_term=su%E5%92%8Csu%20-&spm=1018.2226.3001.4187(总结)Linux下su与su -命令的本质区别http://www.ha97.com/4001.html为什么要获取环境变量(在Linux操作系统下的C/C++程序中为什么要获取环境变量)?
若在Linux操作系统下写一个只能由当前登录用户(以普通用户HJM为例,即环境变量USER的值为普通用户HJM)运行的C/C++程序代码,此时就需要在Linux操作系统下的C/C++程序中获取环境变量,如下所示:
[HJM@hjmlcc ~]$ ls Makefile mycal.c process.c [HJM@hjmlcc ~]$ cat process.c #include<stdio.h> #include<stdlib.h> #include<string.h> int main() { char* id=getenv("USER"); if(strcasecmp(id,"HJM")!=0) //比较时忽略大小写、 { printf("权限拒绝!\n"); } else{ printf("成功执行!\n"); } return 0; } [HJM@hjmlcc ~]$ gcc process.c [HJM@hjmlcc ~]$ ls a.out Makefile mycal.c process.c [HJM@hjmlcc ~]$ whoami HJM [HJM@hjmlcc ~]$ echo $USER HJM [HJM@hjmlcc ~]$ ./a.out 成功执行! [HJM@hjmlcc ~]$ su - Password: Last login: Wed Jan 11 11:32:38 CST 2023 on pts/1 [root@hjmlcc ~]# pwd /root [root@hjmlcc ~]# cd /home/HJM [root@hjmlcc HJM]# ls a.out Makefile mycal.c process.c [root@hjmlcc HJM]# whoami root [root@hjmlcc HJM]# echo $USER root [root@hjmlcc HJM]# ./a.out 权限拒绝! [root@hjmlcc HJM]# su - HJM Last login: Wed Jan 11 11:34:01 CST 2023 on pts/1 [HJM@hjmlcc ~]$ su Password: [root@hjmlcc HJM]# pwd /home/HJM [root@hjmlcc HJM]# whoami root [root@hjmlcc HJM]# echo $USER HJM [root@hjmlcc HJM]# ls a.out Makefile mycal.c process.c [root@hjmlcc HJM]# ./a.out 成功执行! [root@hjmlcc HJM]# su - Last login: Wed Jan 11 11:35:15 CST 2023 on pts/1 [root@hjmlcc ~]# su - LCC Last login: Wed Jan 11 10:52:43 CST 2023 on pts/1 [LCC@hjmlcc ~]$ pwd /home/LCC [LCC@hjmlcc ~]$ whoami LCC [LCC@hjmlcc ~]$ echo $USER LCC [LCC@hjmlcc ~]$ pwd /home/LCC [LCC@hjmlcc ~]$ cd /home/HJM [LCC@hjmlcc HJM]$ ls a.out Makefile mycal.c process.c [LCC@hjmlcc HJM]$ ./a.out 权限拒绝! [LCC@hjmlcc HJM]$ su - Password: Last login: Wed Jan 11 11:58:26 CST 2023 on pts/1 [root@hjmlcc ~]# whoami root [root@hjmlcc ~]# echo $USER root [root@hjmlcc ~]# su LCC [LCC@hjmlcc root]$ whoami LCC [LCC@hjmlcc root]$ echo $USER LCC [LCC@hjmlcc root]$ cd /home/HJM [LCC@hjmlcc HJM]$ ls a.out Makefile mycal.c process.c [LCC@hjmlcc HJM]$ ./a.out 权限拒绝! [LCC@hjmlcc HJM]$ //综上,环境变量一定在某些地方有特殊用途,上面粗略的展示了其中一个方面、 //由上述例子可推断,我们之前学习 权限 时,底层实现逻辑与此处该例子类似、
环境变量的组织方式
每个程序都会收到一张环境表,环境表是一个字符指针数组,每个指针指向一个以字符 '\0' 结尾的环境常量字符串、
环境变量通常具有全局属性
本地(普通)变量只在本Shell内有效,而环境变量则是全局有效,我们从Linux系统的角度来理解一下:
[HJM@hjmlcc ~]$ ls
Makefile mycal.c process.c
[HJM@hjmlcc ~]$ cat process.c
#include<stdio.h>
#include<unistd.h>
int main()
{
while(1)
{
printf("Hello,Lcc,PID:%d,PPID:%d\n",getpid(),getppid());
sleep(2);
}
return 0;
}
[HJM@hjmlcc ~]$ gcc process.c
[HJM@hjmlcc ~]$ ls
a.out Makefile mycal.c process.c
[HJM@hjmlcc ~]$ ./a.out
Hello,Lcc,PID:30455,PPID:26215 //PID:30455,PPID:26215
Hello,Lcc,PID:30455,PPID:26215
^C
[HJM@hjmlcc ~]$ ./a.out
Hello,Lcc,PID:30472,PPID:26215 //PID:30472,PPID:26215
Hello,Lcc,PID:30472,PPID:26215
^C
[HJM@hjmlcc ~]$ ./a.out
Hello,Lcc,PID:30480,PPID:26215 //PID:30480,PPID:26215
Hello,Lcc,PID:30480,PPID:26215
^C
[HJM@hjmlcc ~]$
//1、
//当运行一个可执行程序时则会得到一个进程,Linux操作系统会为其分配PID,结束(退出)该进程,再次运
//行该可执行程序时又会得到一个新的进程,这两个进程并不是同一个进程,Linux操作系统则会为新的进
//程重新分配PID,所以会得到两个不同的PID,但是这两个进程的父进程是同一个进程(bash进程),所以
//PPID的值是不会发生改变的,其中,当我们打开Xshell并登录用户时,此时bash进程就会被启动,直
//到Xshell被关闭退出时,bash进程才会结束(退出),在此期间,bash进程是一直时实运行的,bash进程就
//是当我们打开Xshell并登录用户时,Linux操作系统给我们创建的一个命令行解释器、
//2、
//若在另外一个SSH渠道中通过指令kill -9 26215来杀掉原来的SSH渠道中的bash进程,则此时在原来的
//SSH渠道中再输入的任何内容都将不会再有任何反应,相当于该SSH渠道下的Linux命令行就挂掉了,这是
//因为,我们在Linux命令行中正常使用指令时(指令又是可执行程序,使用指令就相当于是运行可执行程序
//因此会得到一个进程),这些指令本质上是需要首先被其对应的bash进程(父进程)所获取的,当其对应的
//bash进程(父进程)被杀掉后,我们再使用这些指令时,其对应的bash进程(父进程)就无法正常的获取这些
//指令,因此,这些指令将不再能够正确地发挥其作用了、
//3、
//bash也是一个进程,bash进程所对应的可执行程序所存在的路径为:/usr/bin,当每一次通过Xshell或者其
//他登录软件进行用户登录时,Linux操作系统就会为当前用户创建一个bash进程,因此当我们再一次打
//开Xshell,登录用户时,Linux操作系统就会为当前用户创建一个新的bash进程,再运行可执行程序
//a.out时,该新的bash进程就是该运行可执行程序a.out所得到的进程的父进程,此时PPID就会发生变化,
//因为此时的bash进程和之前的bash进程并不是同一个进程,所以,PPID的值会发生改变、
[HJM@hjmlcc ~]$ ls /usr/bin/bash
/usr/bin/bash
[HJM@hjmlcc ~]$
//4、
//bash进程也是使用C/C++写的,因此我们可以使用bash进程对应的可执行程序对应的代码中的scanf或
//cin来获取当前用户在Linux命令行中输入的内容、
//5、
//若打开Xshell登录用户后,新建多个会话,则Linux操作系统会为多个会话各自创建一个bash进程,这多
//个bash进程并不是同一个进程,所以这多个bash进程的PID的值是不同的、
在Linux命令行中启动的进程(包括运行自己生成的可执行程序与运行系统的可执行程序所得到的进程)的父进程全部都是bash进程,bash进程的底层逻辑也是通过fork的形式来创建子进程的,至于fork之后,Linux操作系统是如何将自己生成的可执行程序或系统的可执行程序加载进来并被运行从而形成进程的,以及bash进程的子进程又是如何被启动并为子进程传递Linux命令行参数和环境变量参数的问题,在后期的进程控制中再进行具体的阐述、
环境变量通常具有全局属性:
[HJM@hjmlcc ~]$ ls
Makefile mycal.c process.c
[HJM@hjmlcc ~]$ cat process.c
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
int main()
{
while(1)
{
printf("Hello,Lcc,PID:%d,PPID:%d,myenv=%s\n",getpid(),getppid(),getenv("LCC"));
sleep(1);
}
return 0;
}
[HJM@hjmlcc ~]$ gcc process.c
[HJM@hjmlcc ~]$ ls
a.out Makefile mycal.c process.c
[HJM@hjmlcc ~]$ ./a.out
Hello,Lcc,PID:29911,PPID:9456,myenv=(null) //不存在环境变量LCC、
Hello,Lcc,PID:29911,PPID:9456,myenv=(null)
Hello,Lcc,PID:29911,PPID:9456,myenv=(null)
Hello,Lcc,PID:29911,PPID:9456,myenv=(null)
Hello,Lcc,PID:29911,PPID:9456,myenv=(null)
^C
[HJM@hjmlcc ~]$ LCC=100 //定义在bash进程内部的本地(普通)变量、
[HJM@hjmlcc ~]$ gcc process.c
[HJM@hjmlcc ~]$ ls
a.out Makefile mycal.c process.c
[HJM@hjmlcc ~]$ ./a.out
//此时在Linux命令行中运行可执行程序a.out则会启动一个进程,该进程的父进程是bash进程,而本地(普通
//)变量LCC定义在bash进程的内部,但本地(普通)变量不能被子进程继承下去,所以对于bash进程的子进
//程(此处由运行可执行程序a.out所得的进程)而言,是不能从bash进程内部继承到本地(普通)变量LCC的,
//所以此处对于该子进程而言,仍然不存在环境变量LCC、
Hello,Lcc,PID:30316,PPID:9456,myenv=(null)
Hello,Lcc,PID:30316,PPID:9456,myenv=(null) //不存在环境变量LCC、
Hello,Lcc,PID:30316,PPID:9456,myenv=(null)
^C
[HJM@hjmlcc ~]$ echo $LCC
100
[HJM@hjmlcc ~]$ env | grep LCC
[HJM@hjmlcc ~]$ set | grep LCC
LCC=100
[HJM@hjmlcc ~]$ export LCC
//将定义好的本地(普通)变量导出,该本地(普通)变量就会变成环境变量,该环境变量也相当于是定义在bash进程内部的、
[HJM@hjmlcc ~]$ echo $LCC
100
[HJM@hjmlcc ~]$ set | grep LCC
LCC=100
[HJM@hjmlcc ~]$ env | grep LCC
LCC=100
[HJM@hjmlcc ~]$ ./a.out
Hello,Lcc,PID:31302,PPID:9456,myenv=100 //存在环境变量LCC、
Hello,Lcc,PID:31302,PPID:9456,myenv=100
Hello,Lcc,PID:31302,PPID:9456,myenv=100
^C
[HJM@hjmlcc ~]$
//此时在Linux命令行中运行可执行程序a.out则会启动一个进程,该进程的父进程是bash进程,环境变量
//LCC定义在bash进程的内部,但环境变量能够被子进程继承下去,所以对于bash进程的子进程(此处由运行
//可执行程序a.out所得的进程)而言,是能够从bash进程内部继承到环境变量LCC的,所以此处对于该子进
//程而言,可以使用环境变量LCC,故得出上述结果、
总结:
当我们打开Xshell,登录用户后,bash进程便被启动了,我们此时所在的页面就是在bash进程内部,因此,在Linux命令行中定义的本地(普通)变量或环境变量都是在bash进程内部定义的,而所谓的环境变量具有全局性,在全局均有效的意思即指:环境变量是能够被子进程继承下去的!!!,若我们在Linux命令行中定义了一个环境变量,即相当于在bash进程的内部定义了一个环境变量,又因环境变量能够被子进程继承,所以,该环境变量会被bash进程的所有子进程均继承,而对于bash进程的子进程而言,也存在与之对应的子进程,并且环境变量能够被子进程继承,所以该环境变量能够被bash进程的子进程的子进程继承,由此可知,若在bash进程内部定义了一个环境变量,则该环境变量会被bash进程的所有子进程继承,所谓继承也即代表着能够使用该环境变量,由于在bash进程的内部定义的该环境变量,所以对于bash进程而言可以使用该环境变量,再由上可知,bash进程的所有的子进程均能使用该定义在bash进程内部的环境变量,总结来说就是,bash进程以及bash进程后面所有的子进程均可使用该定义在bash进程内部的环境变量,这就是所谓的环境变量具有全局性,即在全局均有效、
在Linux命令行中定义的本地(普通)变量也是在bash进程的内部定义的,但是本地(普通)变量不会被子进程继承下去,也就是说,由于本地(普通)变量是在bash进程内部定义的,所以在bash进程的内部是可以使用该本地(普通)变量的,但又因为本地(普通)变量不会被子进程继承下去,所以在bash进程的所有子进程中(bash进程除外)都不能使用该本地(普通)变量,只能在bash进程内部使用该本地(普通)变量,所谓的本地(普通)变量只在本Shell内有效,即指本地(普通)变量只能在bash进程内部有效,要注意,此处不考虑bash进程与其所有子进程的包含与被包含关系,默认为bash进程与其所有的子进程之间两两互相独立、
残留问题:
在Linux命令行中输入的所有内容,一定需要先被bash进程读取,在bash进程内部对这些内容进行分析,将本地(普通)变量和环境变量保存在bash进程内部的变量列表中,保存在bash进程内部的进程上下文中、
[HJM@hjmlcc ~]$ ls Makefile mycal.c process.c [HJM@hjmlcc ~]$ local_val=hello //本地(普通)变量、 [HJM@hjmlcc ~]$ echo $local_val hello [HJM@hjmlcc ~]$ //问: //由上可知,变量loacl_val是本地(普通)变量,echo指令也是可执行程序,运行(使用)他的时候将会启动一个 //进程,而在Linux命令行中启动的进程的父进程都是bash进程,所以通过指令echo $local_val得到的进程 //是bash进程的子进程,我们又知道,对于普通(本地)变量local_val而言,是定义在bash进程内部的,并且本 //地(普通)变量不能被子进程继承,那么为什么通过指令echo $local_val得到的进程(bash进程的子进程) //能够读取到该本地(普通)变量local_val的值并将其打印在屏幕上的呢? //答: //在Linux操作系统中,在Linux命令行中运行的指令大部分都是以bash进程的子进程(先创建子进程,让子进 //程完成这些指令相对应的功能)的方式执行的,但是还存在一部分在Linux命令行中运行的指令并不是通 //过bash进程的子进程的方式进行执行的,而是由bash进程自己运行(不会创建子进程,而是bash进程直接调 //用自己对应的函数来完成特定的功能),比如指令:echo,set,exoprt,cd等等,我们称这类指令为内建命令, //即一些bash进程非常信任的命令,此时bash进程并不会通过其子进程帮其运行这些命令,而是bash进程自己 //运行这些命令,对于指令:set | grep local_val的原理也是如上、
补充:关于指令:export 和 cd 为什么是内建命令的原因请见视频:
进程地址空间:00:23:00 - 00:26:10、