初识环境变量

问题:环境变量是什么?有什么意义?
int create_process(char *path, char *args[])
{
    int ret = fork();

    if (ret == 0) {
        execve(path, args, NULL);
    }

    return ret;
}

main 函数(默认进程入口)

  • int main(int argc, char *argv[], char *env[])

    • argc 命令行参数个数 (启动参数)
    • argv[] - 命令行参数数组 (启动参数)
    • env[] - 环境变量数组(最后一个元素为 NULL)

什么是环境变量

  • 环境变量是进程运行过程中可能用到的 "键值对" (NAME = Value)
  • 进程拥有一个环境表(environment list),环境表包含了环境变量
  • 环境表用于记录系统中相对固定的共享信息(不特定于具体进程)
  • 进程之间的环境表互相独立(环境表可在父子进程之间传递)

环境表的构成

image.png

下面的程序输出什么?为什么?

parent.c
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>

#define EXE "child.out"

int create_process(char *path, char *args[], char *env[])
{
    int ret = fork();

    if (ret == 0) {
        execve(path, args, env);
    }

    return ret;
}


int main()
{
    char path[] = EXE;
    char arg1[] = "hello";
    char arg2[] = "world";
    char *args[] = {path, arg1, arg2, NULL};

    printf("%d begin\n", getpid());

    printf("%d child = %d\n", getpid(), create_process(EXE, args, args));

    printf("%d end\n", getpid());

    return 0;
}
child.c
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>

int main(int argc, char *argv[], char *env[])
{
    int i = 0;

    sleep(1);

    printf("process parameter:\n");

    for (i=0; i<argc; ++i) {
        printf("exec = %d %s\n", getpid(), argv[i]);
    }

    printf("enviroment list:\n");

    i = 0;
    while (env[i]) {
        printf("exec = %d %s\n", getpid(), env[i++]);
    }

    return 0;
}
tiansong@tiansong:~/Desktop/linux$ ./parent.out 
3581 begin
3581 child = 3582
3581 end
tiansong@tiansong:~/Desktop/linux$ process parameter:
exec = 3582 child.out
exec = 3582 hello
exec = 3582 world
enviroment list:
exec = 3582 child.out
exec = 3582 hello
exec = 3582 world
tiansong@tiansong:~/Desktop/linux$ ./child.out a b c
process parameter:
exec = 3641 ./child.out
exec = 3641 a
exec = 3641 b
exec = 3641 c
enviroment list:
exec = 3641 SHELL=/bin/bash
exec = 3641 COLORTERM=truecolor
exec = 3641 TERM_PROGRAM_VERSION=1.82.1
exec = 3641 LC_ADDRESS=zh_CN.UTF-8
exec = 3641 LC_NAME=zh_CN.UTF-8
exec = 3641 LC_MONETARY=zh_CN.UTF-8
exec = 3641 PWD=/home/tiansong/Desktop/linux
exec = 3641 LOGNAME=tiansong
exec = 3641 XDG_SESSION_TYPE=tty
exec = 3641 VSCODE_GIT_ASKPASS_NODE=/home/tiansong/.vscode-server/bin/6509174151d557a81c9d0b5f8a5a1e9274db5585/node
exec = 3641 MOTD_SHOWN=pam
exec = 3641 HOME=/home/tiansong
exec = 3641 LANG=en_US.UTF-8
exec = 3641 LC_PAPER=zh_CN.UTF-8
exec = 3641 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=00: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:*.dz=01;31:*.gz=01;31:*.lrz=01;31:*.lz=01;31:*.lzo=01;31:*.xz=01;31:*.zst=01;31:*.tzst=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:*.wim=01;31:*.swm=01;31:*.dwm=01;31:*.esd=01;31:*.jpg=01;35:*.jpeg=01;35:*.mjpg=01;35:*.mjpeg=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:*.ogv=01;35:*.ogx=01;35:*.aac=00;36:*.au=00;36:*.flac=00;36:*.m4a=00;36:*.mid=00;36:*.midi=00;36:*.mka=00;36:*.mp3=00;36:*.mpc=00;36:*.ogg=00;36:*.ra=00;36:*.wav=00;36:*.oga=00;36:*.opus=00;36:*.spx=00;36:*.xspf=00;36:
exec = 3641 GIT_ASKPASS=/home/tiansong/.vscode-server/bin/6509174151d557a81c9d0b5f8a5a1e9274db5585/extensions/git/dist/askpass.sh
exec = 3641 SSH_CONNECTION=192.168.112.1 6125 192.168.112.128 22
exec = 3641 VSCODE_GIT_ASKPASS_EXTRA_ARGS=
exec = 3641 LESSCLOSE=/usr/bin/lesspipe %s %s
exec = 3641 XDG_SESSION_CLASS=user
exec = 3641 TERM=xterm-256color
exec = 3641 LC_IDENTIFICATION=zh_CN.UTF-8
exec = 3641 LESSOPEN=| /usr/bin/lesspipe %s
exec = 3641 USER=tiansong
exec = 3641 VSCODE_GIT_IPC_HANDLE=/run/user/1000/vscode-git-9f60bfa27b.sock
exec = 3641 SHLVL=2
exec = 3641 LC_TELEPHONE=zh_CN.UTF-8
exec = 3641 LC_MEASUREMENT=zh_CN.UTF-8
exec = 3641 XDG_SESSION_ID=7
exec = 3641 XDG_RUNTIME_DIR=/run/user/1000
exec = 3641 SSH_CLIENT=192.168.112.1 6125 22
exec = 3641 LC_TIME=zh_CN.UTF-8
exec = 3641 VSCODE_GIT_ASKPASS_MAIN=/home/tiansong/.vscode-server/bin/6509174151d557a81c9d0b5f8a5a1e9274db5585/extensions/git/dist/askpass-main.js
exec = 3641 XDG_DATA_DIRS=/usr/local/share:/usr/share:/var/lib/snapd/desktop
exec = 3641 BROWSER=/home/tiansong/.vscode-server/bin/6509174151d557a81c9d0b5f8a5a1e9274db5585/bin/helpers/browser.sh
exec = 3641 PATH=/home/tiansong/openharmony/toolchain/gcc-arm-none-eabi-10.3-2021.10/bin:/home/tiansong/openharmony/toolchain/xtensa-esp32-elf:/home/tiansong/openharmony/toolchain/xtensa-esp32s3-elf/bin:/home/tiansong/openharmony/toolchain/gcc_riscv32/bin:/home/tiansong/.local/bin:/home/tiansong/.vscode-server/bin/6509174151d557a81c9d0b5f8a5a1e9274db5585/bin/remote-cli:/home/tiansong/.local/bin:/home/tiansong/openharmony/toolchain/gcc-arm-none-eabi-10.3-2021.10/bin:/home/tiansong/openharmony/toolchain/xtensa-esp32-elf:/home/tiansong/openharmony/toolchain/xtensa-esp32s3-elf/bin:/home/tiansong/openharmony/toolchain/gcc_riscv32/bin:/home/tiansong/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin
exec = 3641 DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus
exec = 3641 LC_NUMERIC=zh_CN.UTF-8
exec = 3641 OLDPWD=/home/tiansong/.vscode-server/bin/6509174151d557a81c9d0b5f8a5a1e9274db5585
exec = 3641 TERM_PROGRAM=vscode
exec = 3641 VSCODE_IPC_HOOK_CLI=/run/user/1000/vscode-ipc-68bc0605-d193-46cd-bf03-3029b6c8175b.sock
exec = 3641 _=./child.out
tiansong@tiansong:~/Desktop/linux$ echo $USER
tiansong
tiansong@tiansong:~/Desktop/linux$ echo $SHLVL
2
tiansong@tiansong:~/Desktop/linux$ echo $PATH
/home/tiansong/openharmony/toolchain/gcc-arm-none-eabi-10.3-2021.10/bin:/home/tiansong/openharmony/toolchain/xtensa-esp32-elf:/home/tiansong/openharmony/toolchain/xtensa-esp32s3-elf/bin:/home/tiansong/openharmony/toolchain/gcc_riscv32/bin:/home/tiansong/.local/bin:/home/tiansong/.vscode-server/bin/6509174151d557a81c9d0b5f8a5a1e9274db5585/bin/remote-cli:/home/tiansong/.local/bin:/home/tiansong/openharmony/toolchain/gcc-arm-none-eabi-10.3-2021.10/bin:/home/tiansong/openharmony/toolchain/xtensa-esp32-elf:/home/tiansong/openharmony/toolchain/xtensa-esp32s3-elf/bin:/home/tiansong/openharmony/toolchain/gcc_riscv32/bin:/home/tiansong/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin

实验表明,环境变量是存储在环境表中的,环境表在代码中是一个字符串数组,字符串数组记录了系统中的关键信息,而这些关键信息可以看坐是特殊的进程参数.

理解环境变量

深入理解环境变量

  • 对于进程来说,环境变量是一种特殊的参数
  • 环境变量相对于启动参数较稳定(系统定义 且 各个进程共享)
  • 环境变量遵守固定规范 (如:键值对,变量名大写)
  • 环境变量 与 启动参数 存储于进程中同一内存区域(私有)

环境变量读写接口

  • 头文件: #include <stdlib.h>
  • 读: char *getenv(const char *name);

    • 返回 name 环境变量的值,如果不存在,返回 NULL
  • 写: int putenv(char *string);

    • 设置 / 改变环境变量 (NAME = Value), string 不能是栈上定义的字符串
    • 如果环境变量存在,改变值; 如果环境变量不存在,则创建
    • putenv("TEST"); → 环境变量被清空 (TEST = NULL)
  • 环境表入口: extern char **environ;

下面的程序输出什么?为什么?

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

extern char **environ;

int main()
{
    int i = 0;

    printf("original:\n");
    printf("%s=%s\n", "TEST1", getenv("TEST1"));
    printf("%s=%s\n", "TEST2", getenv("TEST2"));
    printf("%s=%s\n", "TEST3", getenv("TEST3"));

    putenv("TEST1");
    putenv("TEST2=NEW-VALUE");
    putenv("TEST3=CREATE NEW");

    printf("changed:\n");
    printf("%s=%s\n", "TEST1", getenv("TEST1"));
    printf("%s=%s\n", "TEST2", getenv("TEST2"));
    printf("%s=%s\n", "TEST3", getenv("TEST3"));

    while (environ[i]) {
        printf("exec = %d %s\n", getpid(), environ[i++]);
    }

    return 0;
}
tiansong@tiansong:~/Desktop/linux$ gcc env.c -o env.out
tiansong@tiansong:~/Desktop/linux$ ./env.out 
original:
TEST1=(null)
TEST2=(null)
TEST3=(null)
changed:
TEST1=(null)
TEST2=NEW-VALUE
TEST3=CREATE NEW
exec = 4403 SHELL=/bin/bash
exec = 4403 COLORTERM=truecolor
exec = 4403 TERM_PROGRAM_VERSION=1.82.1
exec = 4403 LC_ADDRESS=zh_CN.UTF-8
exec = 4403 LC_NAME=zh_CN.UTF-8
exec = 4403 LC_MONETARY=zh_CN.UTF-8
exec = 4403 PWD=/home/tiansong/Desktop/linux
exec = 4403 LOGNAME=tiansong
exec = 4403 XDG_SESSION_TYPE=tty
exec = 4403 VSCODE_GIT_ASKPASS_NODE=/home/tiansong/.vscode-server/bin/6509174151d557a81c9d0b5f8a5a1e9274db5585/node
exec = 4403 MOTD_SHOWN=pam
exec = 4403 HOME=/home/tiansong
exec = 4403 LANG=en_US.UTF-8
exec = 4403 LC_PAPER=zh_CN.UTF-8
exec = 4403 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=00: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:*.dz=01;31:*.gz=01;31:*.lrz=01;31:*.lz=01;31:*.lzo=01;31:*.xz=01;31:*.zst=01;31:*.tzst=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:*.wim=01;31:*.swm=01;31:*.dwm=01;31:*.esd=01;31:*.jpg=01;35:*.jpeg=01;35:*.mjpg=01;35:*.mjpeg=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:*.ogv=01;35:*.ogx=01;35:*.aac=00;36:*.au=00;36:*.flac=00;36:*.m4a=00;36:*.mid=00;36:*.midi=00;36:*.mka=00;36:*.mp3=00;36:*.mpc=00;36:*.ogg=00;36:*.ra=00;36:*.wav=00;36:*.oga=00;36:*.opus=00;36:*.spx=00;36:*.xspf=00;36:
exec = 4403 GIT_ASKPASS=/home/tiansong/.vscode-server/bin/6509174151d557a81c9d0b5f8a5a1e9274db5585/extensions/git/dist/askpass.sh
exec = 4403 SSH_CONNECTION=192.168.112.1 6125 192.168.112.128 22
exec = 4403 VSCODE_GIT_ASKPASS_EXTRA_ARGS=
exec = 4403 LESSCLOSE=/usr/bin/lesspipe %s %s
exec = 4403 XDG_SESSION_CLASS=user
exec = 4403 TERM=xterm-256color
exec = 4403 LC_IDENTIFICATION=zh_CN.UTF-8
exec = 4403 LESSOPEN=| /usr/bin/lesspipe %s
exec = 4403 USER=tiansong
exec = 4403 VSCODE_GIT_IPC_HANDLE=/run/user/1000/vscode-git-9f60bfa27b.sock
exec = 4403 SHLVL=2
exec = 4403 LC_TELEPHONE=zh_CN.UTF-8
exec = 4403 LC_MEASUREMENT=zh_CN.UTF-8
exec = 4403 XDG_SESSION_ID=7
exec = 4403 XDG_RUNTIME_DIR=/run/user/1000
exec = 4403 SSH_CLIENT=192.168.112.1 6125 22
exec = 4403 LC_TIME=zh_CN.UTF-8
exec = 4403 VSCODE_GIT_ASKPASS_MAIN=/home/tiansong/.vscode-server/bin/6509174151d557a81c9d0b5f8a5a1e9274db5585/extensions/git/dist/askpass-main.js
exec = 4403 XDG_DATA_DIRS=/usr/local/share:/usr/share:/var/lib/snapd/desktop
exec = 4403 BROWSER=/home/tiansong/.vscode-server/bin/6509174151d557a81c9d0b5f8a5a1e9274db5585/bin/helpers/browser.sh
exec = 4403 PATH=/home/tiansong/openharmony/toolchain/gcc-arm-none-eabi-10.3-2021.10/bin:/home/tiansong/openharmony/toolchain/xtensa-esp32-elf:/home/tiansong/openharmony/toolchain/xtensa-esp32s3-elf/bin:/home/tiansong/openharmony/toolchain/gcc_riscv32/bin:/home/tiansong/.local/bin:/home/tiansong/.vscode-server/bin/6509174151d557a81c9d0b5f8a5a1e9274db5585/bin/remote-cli:/home/tiansong/.local/bin:/home/tiansong/openharmony/toolchain/gcc-arm-none-eabi-10.3-2021.10/bin:/home/tiansong/openharmony/toolchain/xtensa-esp32-elf:/home/tiansong/openharmony/toolchain/xtensa-esp32s3-elf/bin:/home/tiansong/openharmony/toolchain/gcc_riscv32/bin:/home/tiansong/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin
exec = 4403 DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus
exec = 4403 LC_NUMERIC=zh_CN.UTF-8
exec = 4403 OLDPWD=/home/tiansong/.vscode-server/bin/6509174151d557a81c9d0b5f8a5a1e9274db5585
exec = 4403 TERM_PROGRAM=vscode
exec = 4403 VSCODE_IPC_HOOK_CLI=/run/user/1000/vscode-ipc-68bc0605-d193-46cd-bf03-3029b6c8175b.sock
exec = 4403 _=./env.out
exec = 4403 TEST2=NEW-VALUE
exec = 4403 TEST3=CREATE NEW

综合编程小练习

  • 编写应用程序,通过命令行参数读写环境变量
  • 选项定义:

    • -a : 无选项值,输出所有环境变量
    • -r : 读环境变量, -n → 环境变量名
    • -w : 写环境变量, -n → 环境变量名, -v → 环境变量值
    • -t : 环境变量读写测试,先写入指定环境变量,之后输出所有环境变量
main.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>

typedef int OptCall(const char *, const char *);

typedef struct {
    const char opt;
    OptCall *handler;
}CallHandler;

static int A_Handler(const char *n, const char *v)
{
    extern char **environ;
    int i = 0;

    while (environ[i]) {
        printf("%s\n", environ[i++]);
    }

    return 0;
}

static int R_Handler(const char *n, const char *v)
{
    if (n) {
        printf("%s=%s\n", n, getenv(n));
    } else {
        printf("Need environ Name to read value...\n");
    }

    return 0;
}

static int W_Handler(const char *n, const char *v)
{
    int err = 1;

    if (n && v) {
        char *kv = malloc(strlen(n) + strlen(v) + 2);

        if (kv) {
            strcpy(kv, n);
            strcat(kv, "=");
            strcat(kv, v);

            err = putenv(kv);

            if (!err) {
                printf("New Environ: %s\n", kv);
            } else {
                printf("Error on writing new environ value ...\n");
            }
        }

        // free(kv);   // 注意!!此处不能释放
    } else {
        printf("Need Name and value to write value...\n");
    }

    return err;
}

static int T_Handler(const char *n, const char *v)
{
    return W_Handler(n, v) || A_Handler(n, v);
}

static const CallHandler g_handler[] = {
    {'a', A_Handler},
    {'r', R_Handler},
    {'w', W_Handler},
    {'t', T_Handler},
};

static const int g_len = sizeof(g_handler) / sizeof(*g_handler);

int main(int argc, char *argv[])
{
    int c = 0;
    char opt = 0;
    char *name = NULL;
    char *value = NULL;

    while ((c = getopt(argc, argv, "arwtn:v:")) != -1) {
        switch (c)
        {
        case 'a':
        case 'r':
        case 'w':
        case 't':
            opt = c;
            break;
        case 'n':
            name = optarg;
            break;
        case 'v':
            value = optarg;
            break;
        default:
            exit(-1);
        }
    }

    for (c=0; c<g_len; c++) {
        if (opt == g_handler[c].opt) {
            g_handler[c].handler(name, value);
            break;
        }
    }

    return 0;
}
tiansong@tiansong:~/Desktop/linux$ gcc main.c -o main.out
tiansong@tiansong:~/Desktop/linux$ ./main.out -w -n TEST -v 123
New Environ: TEST=123
tiansong@tiansong:~/Desktop/linux$ ./main.out -r -n PWD
PWD=/home/tiansong/Desktop/linux
问题:进行参数 和 环境变量 对进程意味着什么?

TianSong
734 声望138 粉丝

阿里山神木的种子在3000年前已经埋下,今天不过是看到当年注定的结果,为了未来的自己,今天就埋下一颗好种子吧