Linux系统编程Day12 -- 环境变量(初识)
往期内容回顾
进程状态的优先级和特性
进程属性和常见进程
进程管理
理解计算机的软硬件管理
Linux 环境变量详解
前言:为什么要学习环境变量
在 Linux 系统中,环境变量(Environment Variables)是一个非常重要的概念,它们就像系统运行的“配置开关”,决定了程序运行的方式、查找文件的路径、默认语言等。
如果你会正确使用环境变量,不仅可以 高效地配置开发环境,还可以 定制命令行为,甚至在 自动化脚本 中发挥巨大作用。
无论是系统管理员、开发者还是普通 Linux 用户,都应该掌握环境变量的基本概念与用法。
主要学习内容
-
环境变量的概念与作用
-
查看环境变量的方法
-
常见的重要环境变量
-
设置环境变量
-
永久保存环境变量
-
删除环境变量
-
环境变量与 Shell 的关系
-
环境变量在脚本中的应用
1. 环境变量的概念与作用
环境变量是一组 键值对(key=value)的配置,用来影响 Linux 系统和应用程序的运行环境。
-
作用:
-
告诉系统或程序应该在哪个目录找可执行文件(PATH)
-
设置默认语言(LANG)
-
配置用户主目录(HOME)
-
控制临时文件位置(TMPDIR)
-
-
特点:
-
临时的环境变量只在当前会话有效
-
可以通过配置文件设置成永久变量
-
变量名区分大小写,通常是全大写
-
2. 查看环境变量的方法
-
env:显示所有环境变量
env
printenv:查看指定变量
printenv PATH
echo:输出变量值
echo $PATH
3. 常见的重要环境变量
变量名 | 作用 |
---|---|
PATH | 可执行文件的搜索路径 |
HOME | 当前用户主目录 |
USER | 当前用户名 |
SHELL | 当前使用的 Shell 类型 |
LANG | 系统语言设置 |
PWD | 当前工作目录 |
EDITOR | 默认文本编辑器 |
| 邮件存储位置 |
4. 设置环境变量
临时设置(仅在当前会话有效):
export MYVAR="Hello Linux" echo $MYVAR
c语言编译后常常我们在某些条件下无法执行程序,出现“permission denied!”,计算机是如何制定我们无法访问的呢?
c语言里提供了 “getenv()”函数用于获取环境变量。下面有一段代码:
#include<stdio.h> #include<string.h> #include<stdlib.h> #define USER "USER" int main(){char* who = getenv(USER);if(strcmp(who,"root")!=0){printf("Permissiong denied!\n");}else{printf("Hello root User!\n");}}
通过获取USER的环境变量来获取当前使用用户,如果当前用户不是root用户,就输出“permission denied!”,如果是就可以访问:
为什么真实系统会出现 “Permission denied”
当你在 Linux 下运行一个程序时,系统会根据文件权限和用户身份来判断是否允许执行。
判断逻辑大致是:
每个文件在磁盘上有一个 所有者 (owner)、所属组 (group) 和 其他用户 (others) 的权限字段。
这三个类别的权限用 rwx 表示(r=读,w=写,x=执行)。
当你运行程序时,内核会:
获取当前进程的 实际用户ID (UID) 和 有效用户ID (EUID)。
获取目标文件的权限位(stat 系统调用)。
根据 UID 和文件的 owner/group 关系判断是否有执行 (x) 权限。
如果判断结果不允许执行,就返回 EACCES,Shell 就显示:
任何你在 bash 终端里运行的程序,默认就是 bash 的子进程。
你可以用 ps 命令确认进程的父进程 ID (PPID) 来验证关系
ps -f
UID PID PPID C STIME TTY TIME CMD
user 174407 174406 0 17:47 pts/1 00:00:00 -bash
user 175461 174407 0 17:55 pts/1 00:00:00 ps -f
本地变量:
直接在终端输入:
val = 1234;
利用set查看所有shell变量(环境变量和shell变量)
set | grep val
5. 永久保存环境变量
根据作用范围不同,修改不同文件:
-
当前用户:~/.bashrc 或 ~/.bash_profile
-
所有用户:/etc/profile 或 /etc/environment
例如:
echo 'export MYVAR="Hello World"' >> ~/.bashrc source ~/.bashrc
6. 删除环境变量
-
使用 unset 命令:
unset MYVAR
7. 环境变量与 Shell 的关系
-
环境变量本质上是 Shell 的一部分,它们通过 Shell 进程传递给子进程。
-
子进程可以继承父进程的环境变量,但修改不会影响父进程。
-
不同 Shell(bash、zsh、sh)加载环境变量的配置文件路径可能不同
8. 环境变量在脚本中的应用
在 Shell 脚本中可以直接使用环境变量:
#!/bin/bash echo "当前用户:$USER" echo "主目录:$HOME"
运行脚本时,脚本会读取当前 Shell 的环境变量
在c语言,python,c++,matlab等语言中,同样也有着类似于shell的pwd等命令,接下来我们简单实用c语言实现 pwd命令的c接口
#include<stdio.h> #include<stdlib.h> #define PWD "PWD" int main(){printf("%s\n",getenv(PWD)); }
这样我们就实现了一个我们的pwd命令
然后将这段代码的执行文件放入 --> /usr/bin/ 文件路径即可。
[Yajun@iZwz9b70mwpeltilcusk8bZ ~]$ sudo cp PWD /usr/bin/ [sudo] password for Yajun: [Yajun@iZwz9b70mwpeltilcusk8bZ ~]$ PWD /home/Yajun
同样这样我们也可以实现其他的环境变量。
总结
-
环境变量是 Linux 系统运行的“全局配置参数”,掌握它能让你灵活地控制系统和程序的行为。
-
要会 查看(env/printenv/echo)、设置(export)、删除(unset) 以及 永久保存 环境变量。
-
在实际工作中,环境变量在 程序部署、脚本编写、开发环境配置 等方面都有重要作用。
二、命令行参数
前言
当我们在命令行(比如Linux终端、Windows命令提示符)执行一个程序时,除了直接运行程序本身,还可以附加一些参数来影响程序的行为。这些参数被称为命令行参数(Command Line Arguments)。
命令行参数让同一个程序能应对多种需求,而不用每次都改代码重新编译。例如:你可以用参数指定输入文件名、控制程序的运行模式、传递配置信息等等。
C语言作为底层的编程语言,自然提供了对命令行参数的支持。这也让C语言编写的程序可以方便地接收外部输入,从而灵活运行。
1、什么是命令行参数?
在命令行执行程序时,格式通常是:
./myprogram arg1 arg2 arg3 ...
./myprogram 是你要执行的程序
arg1, arg2, arg3 是传递给程序的参数,可以是文件名、数字、字符串等。
这些参数都会被操作系统传递给程序,程序通过特定方式读取和使用它们。
2、C语言中main函数和命令行参数
C语言程序的入口是 main 函数,标准形式允许有两种参数形式:
int main(int argc, char *argv[])
或者
int main(int argc, char **argv)
这两个参数分别代表什么?
-
argc(argument count):整数,代表传给程序的命令行参数个数。
-
包含程序本身的名字,所以至少是1。
-
例如:./myprogram arg1 arg2,则argc为3。
-
-
argv(argument vector):字符串数组,保存所有命令行参数的指针。
-
argv[0] 通常是程序自身的路径或名称,比如 "./myprogram"。
-
argv[1] 是第一个参数 "arg1",argv[2] 是第二个参数 "arg2",以此类推。
-
argv[argc] 是一个空指针 NULL,表示数组结束。
-
main函数参数可以有几个?
-
标准C中,main函数可以写成以下三种形式(常见用法):
int main(void) // 不接收任何命令行参数
int main(int argc, char *argv[]) // 接收命令行参数
int main(int argc, char **argv) // 和上面等价
另外,有些平台允许第三个参数 char *envp[] 用来接收环境变量,但这不是标准,且一般不常用。
下面是一段c语言代码用于测试命令行参数:
#include <stdio.h> #include <stdlib.h> #include <string.h>int main(int argc,char* argv[]){while (argc--) {printf("argc[%d] --> %s\n",argc,*argv);argv++;} }
运行后输出:
argc[0] --> ./cmp
如果你在命令行上输入选项:例如 “66”,“ok”
ENV_VAL % ./cmp "66" "ok"
输出描述:
argc[2] --> ./cmp
argc[1] --> 66
argc[0] --> o
如何理解命令行参数的传递?
操作系统在执行程序时,会将命令行参数打包成一个字符串数组,并传递给程序的 main。这让程序能够按顺序读取每个参数,进行相应处理。
程序可以根据参数的不同执行不同逻辑,常见做法是:
-
检查参数个数是否符合预期
-
遍历参数数组,解析参数内容
-
使用参数值影响程序行为,比如打开指定文件、设置调试模式等
命令行参数的实际使用
#include <stdio.h> #include <stdlib.h> #include <string.h>int main(int argc,char* argv[]){//while (argc--) {// printf("argc[%d] --> %s\n",argc,*argv);//argv++;if(argc != 2){ printf("Usage:\n\t%s[-a/-b/-ab/-ac/-bc/-abc]\n",argv[0]);return 1;} if(strcmp(s1: "-a",s2: argv[1]) == 0){ printf("Function: a\n");} if(strcmp(s1: "-b",s2: argv[1]) == 0){ printf("Function: b\n");} if(strcmp(s1: "-ab",s2: argv[1]) == 0){ printf("Function: ab\n");} if(strcmp(s1: "-ac",s2: argv[1]) == 0){ printf("Function: ac\n");} if(strcmp(s1: "-bc",s2: argv[1]) == 0){ printf("Function: bc\n");} if(strcmp(s1: "-abc",s2: argv[1]) == 0){ printf("Function: abc\n");} }
假设我只运行程序,无输入选项。输出描述:
Usage:
./cmp[-a/-b/-ab/-ac/-bc/-abc]
假如我终端输入是:
./cmp -a
输出:Function: a
第三个参数 char *envp[] 用来接收环境变量,一般不常用里面存放的是系统传入的环境变量。
环境变量作为命令行参数实现:
实现下面这段c语言代码
#include <stdio.h> #include <stdlib.h> #include <string.h>int main(int argc,char* argv[],char* env[]){for(int i = 0;env[i];i++){printf("env[%d] :%s\n",i,env[i]);} }
输出值:
env[0] :TMPDIR=/var/folders/_4/b9p9kkg11r97_ss_c0zt4njm0000gn/T/
env[1] :__CFBundleIdentifier=com.apple.Terminal
env[2] :XPC_FLAGS=0x0
env[3] :TERM=xterm-256color
env[4] :DISPLAY=/private/tmp/com.apple.launchd.aHLlRX784j/org.xquartz:0
env[5] :SSH_AUTH_SOCK=/private/tmp/com.apple.launchd.bfH0Ve9RH4/Listeners
env[6] :XPC_SERVICE_NAME=0
env[7] :TERM_PROGRAM=Apple_Terminal
env[8] :TERM_PROGRAM_VERSION=455.1
env[9] :TERM_SESSION_ID=6F7C1166-D886-453F-9B32-1A8F19F89529
env[10] :SHELL=/bin/zsh
env[11] :HOME=/Users/junye
env[12] :LOGNAME=junye
env[13] :USER=junye
env[14] :PATH=/Users/junye/anaconda3/bin:/Users/junye/anaconda3/condabin:/opt/homebrew/bin:/opt/homebrew/sbin:/Library/Frameworks/Python.framework/Versions/3.12/bin:/usr/local/bin:/System/Cryptexes/App/usr/bin:/usr/bin:/bin:/usr/sbin:/sbin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/local/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/appleinternal/bin:/opt/X11/bin:/Library/Apple/usr/bin
env[15] :SHLVL=1
env[16] :PWD=/Users/junye/Desktop/Linux/ENV_VAL
env[17] :OLDPWD=/Users/junye/Desktop/Linux
env[18] :HOMEBREW_PREFIX=/opt/homebrew
env[19] :HOMEBREW_CELLAR=/opt/homebrew/Cellar
env[20] :HOMEBREW_REPOSITORY=/opt/homebrew
env[21] :INFOPATH=/opt/homebrew/share/info:
env[22] :HOMEBREW_API_DOMAIN=https://mirrors.ustc.edu.cn/homebrew-bottles/api
env[23] :HOMEBREW_BOTTLE_DOMAIN=https://mirrors.ustc.edu.cn/homebrew-bottles/bottles
env[24] :CONDA_EXE=/Users/junye/anaconda3/bin/conda
env[25] :_CE_M=
env[26] :_CE_CONDA=
env[27] :CONDA_PYTHON_EXE=/Users/junye/anaconda3/bin/python
env[28] :CONDA_SHLVL=1
env[29] :CONDA_PREFIX=/Users/junye/anaconda3
env[30] :CONDA_DEFAULT_ENV=base
env[31] :CONDA_PROMPT_MODIFIER=(base)
env[32] :LC_CTYPE=UTF-8
env[33] :_=/Users/junye/Desktop/Linux/ENV_VAL/./cmp
这就是系统导入的环境变量
第二种方法:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdio.h> #include <stdlib.h> extern char** environ; int main(){for(int i = 0; environ[i]!=NULL;i++){printf("ENV[%d]: %s\n", i,environ[i]);} }
这里我们外部导入了 environ 里面存放了许多环境变量,因为环境变量都是字符串,对应的也就是char*,所以envirom是一个二级指针。
第三种方法:getenv获取环境变量
提问:
[Yajun@iZwz9b70mwpeltilcusk8bZ ~]$ hello=888 [Yajun@iZwz9b70mwpeltilcusk8bZ ~]$ env | grep hello [Yajun@iZwz9b70mwpeltilcusk8bZ ~]$ set | grep hello hello=888 [Yajun@iZwz9b70mwpeltilcusk8bZ ~]$ echo $hello 88
echo 是 一个命令,最后运行echo的时候会产生进程,但是hello是一个本地变量,理论上无法被子进程继承。那么为什么echo可以打印出本地变量呢?
解释:
1. 你写的 hello=888 到底是什么变量?
在 Shell 里,直接写 hello=888,这是定义了一个 Shell 的本地变量(也叫普通变量),它只在当前 Shell 进程内部有效,不会自动传递给子进程。
因此你用 set | grep hello 能看到它(set 显示所有本地变量和函数),但 env | grep hello 看不到(env 只显示环境变量,即被导出的变量)。
2. 为什么 echo $hello 能打印变量值
echo 是一个 Shell 内置命令(builtin),它是在当前 Shell 进程内部运行的,不是新启动的子进程。
因此它能直接访问当前 Shell 的本地变量 hello,所以 echo $hello 输出是 888。
3. 为什么 env | grep hello 没有结果
env 命令启动了一个新的子进程,它只会看到已经导出(export)成环境变量的变量。
你的 hello 变量没有用 export hello,所以它不会进入环境变量,子进程 env 看不到。
类型
存储位置
继承给子进程?
说明
本地变量
Shell内部数据结构
否
只在当前Shell进程有效
环境变量
进程的环境内存块
是
由操作系统管理,fork时复制给子进程
导出变量(export)
从本地变量转为环境变量
是
通过export使本地变量变成环境变量
总结一下:
-
环境变量是操作系统为进程维护的一组键值对,表示进程运行的环境信息,父进程的环境变量会被子进程继承,但子进程修改环境变量不会影响父进程。
-
通过 getenv() 函数可以在程序中读取环境变量的值;也可以通过全局变量 environ 访问全部环境变量列表。
-
命令行参数是在启动程序时由操作系统传递给程序的额外输入,C语言通过 main(int argc, char *argv[]) 接收,argc 表示参数个数,argv 是参数字符串数组,其中 argv[0] 是程序名本身。
-
程序通过命令行参数可以灵活调整行为,而环境变量则提供程序运行时的全局配置环境,两者共同作用,增强程序的可配置性和适应性。