《Linux系統程式設計訓練營》5_環境變數程式設計

發表於2023-09-21

初識環境變數

問題:環境變數是什麼?有什麼意義?
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
問題:進行引數 和 環境變數 對程式意味著什麼?

相關文章