寫C/C++程序時,是否注意過main函數的參數?int main(int argc, char *argv[]),以前我們可能覺得這兩個參數可有可無?但我們有沒有想:過程序能不能能接收到外部配置信息?當命令行輸入的指令、程序運行依賴的庫路徑(包括鏈接找動態庫),又是如何被進程識別的?

這些,都涉及一個知識:環境變量。

一.介紹

1.概念

Linux環境變量是系統或用户定義的「k,v結構 鍵值對」,用於為進程提供運行配置(如命令路徑、編碼格式),進程啓動時由內核自動加載,可被所有子進程繼承。

簡單來説,環境變量就是操作系統中用來指定操作系統運行環境的一些參數,對於不用用户有不同的環境變量,系統級環境變量當中通常具有全局特性

作用:環境變量的參數一定有一些特殊用圖,比如記錄家目錄、用户是誰、路徑 等等。

2.常見的環境變量

幾個與環境變量的操作
  1. echo $NAME// 顯示環境變量信息,NAME是環境變量名;
  2. $//用於引入環境變量名
  3. PATH=/root//用PATH舉例: PATH指變量名,/root指路徑,覆蓋寫入環境變量
  4. PATH=$PATH:/root//追加寫入
  5. env :查看系統所有環境變量
常見環境變量:
  • PATH:指定命令的搜索路徑(在我們使用一個命令時,操作系統會先遍歷PATH路徑判斷命令是否存在)

[Linux]為什麼你的程序找不到路徑?答案藏在環境變量中_環境變量

在執行ll時,系統回到PATH路徑下查找ll命令,發現存在->執行;而a.out不存在,操作系統不知道它在哪裏,需要我們指明當前路徑"./':

[Linux]為什麼你的程序找不到路徑?答案藏在環境變量中_環境變量_02

如果我們想讓a.out像系統命令那樣執行,可以把a.out的路徑添加到PATH路徑下:

[Linux]為什麼你的程序找不到路徑?答案藏在環境變量中_內建命令_03

不用擔心覆蓋以後找不到系統命令,因為這個覆蓋是內存級別的,當我們重新用XShell登錄,所更改環境變量都會恢復原樣

最後一個問題,為什麼echo可移執行?

PATH是讓Shell查找“外部命令(或者説可執行程序)”,而echo/cd/pwd等是內置命令

[Linux]為什麼你的程序找不到路徑?答案藏在環境變量中_命令行參數_04

  • HOME:指定用户的主工作目錄(儲存當前登錄永華的個人主目錄),也就是cd ~進入的目錄。
  • SHELL: 當前Shell,目前使用的命令解釋器程序,它的值通常是/bin/bash
  • PWD: 儲存當前用户所在工作目錄路徑,它是實時動態更新的,隨着cd自動變化;我們用./執行a.out時,相當於告訴系統在PWD變量的當前工作目錄下找a.out這個程序。

既然不同用户下環境變量不同,同時C語言提供了調用接口getenv(),我們能不能實現一份代碼(或者一種指令)在不同用户下得到不同的結果?

二.命令行參數

1.介紹

概念:程序運行時通過命令行傳入的字符串參數,用於靈活控制程序的行為。

[Linux]為什麼你的程序找不到路徑?答案藏在環境變量中_環境變量_05

也就是説當我們將我們的代碼編譯成可執行程序時,運行程序的命令./a.out將作為參數被main函數接收(argv),同時我們可以對我們的命令待增加選項:

//./mycmd
#include<iostream>
using namespace std;
#include<stdlib.h>
#include<stdio.h>
#include<string.h>

int main(int argc, char *argv[])
{
    if(argc != 2)//如果參數數量不等於2 
    {
      	//這是我們想通過不同選項達到的不同效果
        //這直接用c++的寫了,cout輸出省事一點
        cout<<"Usage: "<< argv[0] << "-a/b/c"<<endl;
    }
    else if(strcmp(argv[1],"-a")==0)
    {
        cout<<"function1"<<endl;
    }
    else if(strcmp(argv[1],"-b") == 0)
    {
        cout<<"function2"<<endl;
    }
    else if(strcmp(argv[1],"-c")== 0 )
    {
        cout<<"function3"<<endl;
    }
		return 0;
  }

[Linux]為什麼你的程序找不到路徑?答案藏在環境變量中_命令行參數_06

2.原理

就像上面所説,bash解釋器對命令以空格進行分割,將分割後的字符串傳給argv(儲存在向量表中)。

[Linux]為什麼你的程序找不到路徑?答案藏在環境變量中_命令行參數_07

所以,我們使用strcmp(argv[1],"-a")==0去比較,實現不同選項不同效果。

三.命令行參數與環境變量

env

main函數的第三個參數env可以讓我們得到“啓動時”所有的環境變量:

//./mygetenv
#include<stdio.h>
int main(int argc,char *argv[],char *env[])
{
    int i =0;
    for(;env[i];i++)
    {
        printf("env[%d] -> %s\n",i,env[i]);//輸出環境變量
    }
    return 0;
}

除此之外,可以通過第三方變量直接得到環境變量

environ

//./Environ
#include<stdio.h>
int main()
{
    //libc中定義的全局變量量environ指向環境變量表,
    //environ沒有包含在任何頭文件中,所以在使用時 要用extern聲明。
    extren char **environ;
    //通過參數傳遞,得到環境變量
    //還可以通過地址空間
    int i =0;
    for(;environ[i];i++)
    {
        cout<<i<<" "<<environ[i]<<endl;
    }

    return 0;
}

[Linux]為什麼你的程序找不到路徑?答案藏在環境變量中_內建命令_08

Linux下,每個程序都有一張獨立的環境變量表environ,在fork()創建子進程時這張表會被複制,每個進程間環境變量表互不影響;

getenv()

getenv()是讓我們獲取特定的一個環境變量的值

#include <stdio.h>
#include <stdlib.h>
int main()
{
 printf("%s\n", getenv("PATH"));//得到PATH
 return 0;
}

兩張核心向量表

目前為止,我們瞭解到linux中兩張核心的向量表:

  • 命令行參數表
  • 定義:程序運行時用户手動傳入的參數集合。
  • 載體: main(int argc, char* argv[]) , argc 記參數個數(含程序路徑), argv[] 存具體參數。
  • 特性:按約定順序傳入,僅當前運行週期有效。
  •  環境變量表
  • 定義:存儲程序運行所需系統/用户配置的集合。
  • 載體:全局變量 char** environ ,元素為“鍵=值”格式字符串。
  • 特性:按鍵匹配取值,子進程可繼承,支持長期/臨時生效。

四.環境變量

1.環境變量具有全局屬性

操作系統的"第一份"環境變量由內核初始化;我們所運行的進程,一般情況下都是子進程;bash啓動時,會從操作系統的配置文件中讀取環境變量信息,之後在bash上運行我們自己的子進程會繼承父進程的環境變量;再在子進程上創建子進程也是一樣會獲得同樣的環境變量——所以説,環境變量具有全局屬性。

2.添加環境變量

export: 添加環境變量

[Linux]為什麼你的程序找不到路徑?答案藏在環境變量中_本地變量_09

export創建的環境變量,會在之後的進程中不斷被繼承下去;

unset:取消環境變量

命令行直接unset MY_VAL就會發現我們的環境變量消失了;不只是我們自己添加的,原本存在的也可以被取消,unset PATH等等

[Linux]為什麼你的程序找不到路徑?答案藏在環境變量中_內建命令_10

3.本地變量

[Linux]為什麼你的程序找不到路徑?答案藏在環境變量中_本地變量_11

如果我們直接MY_VAL=123這樣去添加環境變量,會發現echo也能查找到,這與export有什麼區別?

但如果我們env去查看,又發現MY_VAL並不存在,為什麼?

這是因為,用 變量名=值添加出的變量是本地變量。

本地變量:僅在當前Shell會話/腳本代碼塊(如if、for) 內生效的變量,不影響其他Shell進程,關閉會話或退出代碼塊後自動失效。

本地變量是不能被子進程繼承的,對於查看所有變量(包括本地變量),可以使用setecho $變量名查看特定變量

五.擴展:內建命令

指令通常被分為兩種:

  1. 常規命令:通過創建子進程完成
  2. 內建命令:bash不創建子進程,而是由自己親自執行(類似於bash調用了自己的或者是系統提供的函數)

echo、pwd、export、cd等是常見的內建命令;

它的關鍵意義主要在於兩點:

  • 提升效率:避免進程創建/銷燬的資源消耗,執行速度遠快於外部命令(如 cd 、 echo 等高頻命令需快速響應)。
  • 操作Shell內部狀態:可直接修改Shell環境(如 echo 可以查看不被子進程繼承的本地變量),外部命令無法操作Shell內部數據。

總結

  1. 程序運行需要兩張表:①命令行參數②環境變量;這兩張表由bash(系統)維護;
  2. main也是函數,也可以傳參,可以被bash調用(得到兩張表的內容)

除此之外,若想對環境變量有更通透的理解,一篇文章當然不夠,需要我們主動尋找,在不同場景下實踐積累。