深入解析環境變量:從基礎概念到系統級應用

目錄

一、基本概念及其核心作用

1、基本概念

2、核心作用

二、常見環境變量

三、查看環境變量方法

四、測試PATH

1、對比執行:./project和直接執行project的區別

2、思考:為何某些命令可直接執行而無需路徑,但我們的二進制程序卻需要指定完整路徑?

3、PATH 解析

各目錄的作用

關鍵點

1. 查找順序

2.?sbin?vs?bin

3. 用戶級目錄

4、方法一:將可執行程序拷貝到環境變量PATH的某一路徑下

5、方式二:將可執行程序所在的目錄導入到環境變量PATH當中

6、關于export PATH=$PATH:/home/hmz的一些小知識

具體解析

注意事項(重要!!!)

永久生效的方法(以?~/.bashrc?為例):

五、測試HOME

1、分別以root和普通用戶執行echo $HOME,對比輸出差異

普通用戶:

超級用戶:

2、執行cd ~; pwd,理解~與$HOME的關系

1.?~?的作用

2.?$HOME?的作用

3.?~?與?$HOME?的關系

4.?示例驗證

腳本內容 (compare_home_tilde.sh)

使用方法

5.?注意事項

總結

六、測試SHELL

七、環境變量相關命令

1、echo:顯示指定環境變量的值

2、export:設置新的環境變量

1?? 設置臨時環境變量

2?? 查看變量值

3?? 驗證是環境變量(對子進程可見)

命令分解解釋

為什么用這個命令測試?export?

4?? 刪除環境變量(下面會介紹)

5?? 驗證已刪除

3、env:顯示所有環境變量

環境變量說明

4、unset:刪除環境變量

1. 添加測試環境變量

2. 驗證變量是否存在

3. 使用?unset?清除變量

4. 再次驗證變量

5. 檢查是否從環境變量中移除

關鍵說明:

5、set:顯示本地定義的Shell變量和環境變量

1.?查看所有變量(快速測試)

2.?開啟/關閉調試模式

📌?總結

八、環境變量的組織方式

九、main函數的參數(超重點!!!)

1、無參數形式

2、帶參數形式

參數解析:

argc(Argument Count)

argv(Argument Vector)

示例代碼:

運行示例:

1. 參數切分的參與者

2. 關鍵細節

3、擴展:envp(環境變量,非標準)

注意事項:

4、編寫命令選項選擇

十、_start函數與main函數的關系

1、_start:程序的真正入口

2、main:開發者定義的入口

3、關鍵區別

4、示例流程

5. 特殊情況

6、總結

十一、通過代碼獲取環境變量

方法一:使用命令行第三個參數

方法二:通過第三方變量environ獲取

十二、通過系統調用獲取或設置環境變量

getenv?和?putenv?函數解析

1.?getenv?函數(獲取環境變量)

函數原型

功能

示例

輸出示例

注意事項

2.?putenv?函數(設置/修改環境變量)

函數原型

功能

示例

輸出示例

注意事項

3. 主要區別

4. 綜合示例

輸出示例

5. 安全注意事項

6. 替代方案(POSIX)

示例

輸出

7、總結

十三、環境變量通常具備全局屬性

1、核心知識點

環境變量的全局特性

(1) 初次運行程序無輸出

(2) 導出環境變量

(3) 再次運行程序輸出結果

2、思考現象背后的原理:為什么會出現這種現象?

進程環境變量繼承機制

為什么需要?export?

十四、環境變量和本地變量

1、作用范圍

2、定義與導出方式

環境變量

本地變量

3、子進程繼承性

4、常見用途

5、查看與刪除

6、擴展知識

7、總結

十五、關于內置命令的必知知識

1、內置命令的特點

2、常見內置命令分類

1. 基礎操作

2. 變量管理

3. 流程控制

4. 作業控制

3、如何判斷命令是否為內置?

4、與外部命令的區別

5、注意事項


一、基本概念及其核心作用

1、基本概念

????????環境變量(Environment Variables)是操作系統中用于動態配置程序運行環境的一種機制。它們以鍵值對(Key-Value)的形式存儲,可以被操作系統、應用程序或腳本讀取和修改。

舉例:

????????在C/C++編程時,雖然我們不知道動態/靜態庫的具體位置,但鏈接仍能成功,正是因為有環境變量幫助編譯器定位這些庫文件

  • 定義:環境變量(environment variables)是操作系統或用戶定義的用于指定運行環境參數的全局變量,用于存儲系統路徑、配置參數、臨時數據等。

  • 作用范圍

    • 系統級:影響所有用戶和進程(如?PATH)。

    • 用戶級:僅對當前用戶生效(如?HOME)。

    • 進程級:由父進程傳遞給子進程(如通過腳本設置的變量)

2、核心作用

  • 配置管理:避免硬編碼路徑或參數(如數據庫連接字符串)。

  • 跨進程通信:父進程通過環境變量向子進程傳遞信息。

  • 系統行為控制:例如?NODE_ENV=production?決定 Node.js 運行模式。

  • 簡化命令:將常用目錄加入?PATH?后可直接執行程序(如?pythongit)。


二、常見環境變量

變量名作用
PATH定義可執行文件的搜索路徑,多個路徑用分隔符(:;)分隔。
HOME當前用戶的主目錄路徑(如 Linux 的?/home/user)。
USER/USERNAME當前登錄的用戶名。
TEMP/TMP臨時文件目錄路徑(如 Windows 的?C:\Temp)。
LANG系統語言和編碼(如?en_US.UTF-8)。
JAVA_HOME指向 Java 安裝路徑,供開發工具(如 Maven)使用。

SHELL

當前Shell,它的值通常是/bin/bash。

三、查看環境變量方法

我們可以通過echo命令來查看環境變量,方式如下:

echo?$NAME?//NAME為待查看的環境變量名稱

例如,查看環境變量PATH:

echo $PATH


四、測試PATH

1、對比執行:./project和直接執行project的區別

如下,你是否想過,為什么直接輸入ls就能運行,而運行自己編譯的程序卻必須加上./前綴?

2、思考:為何某些命令可直接執行而無需路徑,但我們的二進制程序卻需要指定完整路徑?

????????系統默認能直接執行某些命令(如ls)而無需添加路徑,是因為這些命令的存儲位置已被記錄在環境變量PATH中。當用戶輸入命令時,系統會自動搜索PATH中指定的目錄來查找對應程序。

????????但對于用戶自己創建的可執行程序,由于不在PATH包含的目錄中,必須通過添加./前綴明確指明程序位于當前目錄。我們可以通過查看PATH環境變量來確認系統默認的搜索路徑:

????????環境變量PATH包含多個由冒號分隔的路徑。執行ls命令時,Shell會按照從左到右的順序依次在這些路徑中進行查找。

3、PATH 解析

各目錄的作用

目錄用途適用場景
/usr/local/bin手動安裝的第三方軟件make install、手動編譯的程序
/usr/bin系統核心命令和包管理器安裝的軟件lsgreppython3(系統自帶)
/usr/local/sbin手動安裝的系統管理命令如手動編譯的?nginxredis-server
/usr/sbin系統自帶的管理員命令iptablesuseradd(通常需要?sudo
~/.local/bin用戶級 Python/Rust/Node.js 工具pip install --usercargo install
~/bin用戶自定義腳本和程序個人使用的 Shell 腳本、手動放置的可執行文件

關鍵點

1. 查找順序

  • Shell 會從左到右查找命令,第一個匹配到的可執行文件會被執行
    示例

    • 如果?/usr/local/bin/python3?和?/usr/bin/python3?同時存在,會優先使用?/usr/local/bin/python3

    • 如果?~/bin/ls?用戶自定義腳本和程序)存在,它會覆蓋系統的?/usr/bin/ls(系統核心命令和包管理器安裝的軟件(不建議這樣做)。

2.?sbin?vs?bin

  • bin(binary):普通用戶可用的命令(如?lspython3)。

  • sbin(system binary):系統管理命令,通常需要?sudo(如?iptablesuseradd)。

3. 用戶級目錄

  • ~/.local/bin

    • 由?pip install --usercargo installnpm install -g(部分情況)等工具使用。

    • 如果命令無法運行,可能是?~/.local/bin?不在?PATH?中(但你的環境已經包含)。

  • ~/bin

    • 用戶自定義目錄,適合存放個人腳本(如?mybackup.sh)。

    • 如果不存在,可以手動創建:

      mkdir -p ~/bin
      echo 'export PATH=$PATH:~/bin' >> ~/.bashrc
      source ~/.bashrc

????????由于ls命令本身位于PATH的某個路徑下,因此即使不指定完整路徑,系統也能成功找到并執行該命令:

如何讓我們的可執行程序也能不帶路徑直接運行?

有兩種方法可以實現:

4、方法一:將可執行程序拷貝到環境變量PATH的某一路徑下

????????因為系統會默認搜索PATH中的路徑,所以只需把可執行文件放到PATH包含的任意目錄下,之后就可以直接輸入程序名運行了。

sudo cp project /usr/bin

5、方式二:將可執行程序所在的目錄導入到環境變量PATH當中

????????將可執行程序所在的目錄導入到環境變量PATH當中,這樣可以當沒有指定路徑時,系統就會來到該目錄下進行查找:

export PATH=$PATH:/home/hmz

????????將可執行程序所在的目錄導入到環境變量PATH當中后(我們對比PATH原來和現在的樣子,找出不同點),位于該目錄下的可執行程序也就可以在不帶路徑的情況下執行了:

6、關于export PATH=$PATH:/home/hmz的一些小知識

????????這個命令的作用是將?/home/hmz?目錄添加到系統的?PATH?環境變量中,使得在該目錄下的可執行文件可以在任何位置直接運行,而不需要輸入完整路徑。

具體解析

  1. export:這是一個 shell 命令,用于設置或導出環境變量,使其對當前 shell 及其子進程生效。

  2. PATH:PATH?是一個環境變量,存儲了一系列目錄路徑,系統會在這些路徑中查找可執行文件。多個路徑之間用?:?分隔。

  3. $PATH:$PATH?表示當前?PATH?變量的值。例如,默認情況下,PATH?可能包含?/usr/local/bin:/usr/bin:/bin?等路徑。

  4. :/home/hmz:這里將?/home/hmz?追加到?PATH?變量的末尾,用?:?分隔。

  5. PATH=$PATH:/home/hmz:這個賦值語句將新的?PATH?值設置為原來的?PATH?加上?:/home/hmz

  6. export PATH=$PATH:/home/hmz:最終,export?將這個修改后的?PATH?變量導出,使其生效。

這樣,當輸入一個命令時,shell 會依次在下面的路徑中查找可執行文件。

注意事項(重要!!!)

  • 臨時生效:這種方式只在當前 shell 會話中有效,關閉終端或新開終端后,PATH?會恢復默認值。
    如果要永久生效,可以將其添加到?~/.bashrc~/.bash_profile?或?/etc/environment(系統級)中。

  • 路徑順序PATH?的查找順序是從左到右的。如果?/home/hmz?下有與系統命令同名的可執行文件,可能會導致預期外的行為,所以最好不要有與系統命令同名的可執行文件。

  • 安全性:不建議將當前目錄?.?或普通用戶目錄加入?PATH以免惡意程序覆蓋系統命令,造成系統污染。

永久生效的方法(以?~/.bashrc?為例):

echo 'export PATH=$PATH:/home/hmz' >> ~/.bashrc
source ~/.bashrc  # 使更改立即生效

這樣每次打開終端時,PATH?都會自動包含?/home/hmz

????????之后有空再探討其他無需指定路徑即可運行程序的方法,這個超綱了,現在我先學好要學的先,可以先看一下這些方法的表格:

方法適用場景持久性是否需要修改?PATH
符號鏈接單文件程序永久否(需鏈接到?PATH?目錄)
別名簡化長命令需寫入配置文件
函數需要參數處理的復雜命令需寫入配置文件
桌面快捷方式GUI 程序永久
環境變量擴展臨時替代長路徑可選
包管理器安裝系統級工具永久自動配置
標準化命名個人腳本永久需?PATH?包含目錄
exec?命令包裝腳本(無子進程開銷)永久需?PATH?包含目錄

五、測試HOME

????????每個用戶登錄系統時都會自動進入自己的主工作目錄(家目錄),該路徑存儲在HOME環境變量中。

echo $HOME

1、分別以root和普通用戶執行echo $HOME,對比輸出差異

普通用戶:

超級用戶:

2、執行cd ~; pwd,理解~$HOME的關系

????????在Linux操作系統中,~(波浪號)和$HOME環境變量之間存在緊密關聯,它們都指向當前用戶的家目錄(Home Directory)。以下是具體解釋和示例:

1.?~?的作用

  • ~?是一個Shell擴展符號(稱為Tilde Expansion),默認代表當前用戶的家目錄路徑。

    • 例如:cd ~?等同于?cd /home/username(假設用戶名是username)。

    • 若在~后接用戶名(如~otheruser),則指向其他用戶的家目錄(如/home/otheruser)。(之前在cd命令的博客中沒有提及到,現在補充上)

2.?$HOME?的作用

  • $HOME?是一個環境變量,存儲當前用戶家目錄的絕對路徑。

    • 可通過?echo $HOME?查看其值,通常輸出如?/home/username

    • 在腳本或命令中直接使用$HOME時,Shell會將其替換為實際路徑。

3.?~?與?$HOME?的關系

  • 等價性:在大多數情況下,~$HOME指向同一路徑。

    • 執行?cd ~; pwd?和?cd $HOME; pwd?的輸出結果相同。

  • 差異:

    • ~?是Shell提供的快捷方式,由Shell在解析命令時直接擴展。

    • $HOME?是環境變量,通過變量替換機制實現。

    • 某些場景下(如字符串中未引用的~),Shell可能不會展開~,而$HOME始終作為變量處理。

4.?示例驗證

腳本內容 (compare_home_tilde.sh)

#!/bin/bash# 比較 ~ 和 $HOME 是否相同
if [ ~ = "$HOME" ]; thenecho "They are identical: ~ points to $HOME"
elseecho "They are different: ~ is '~', but HOME is '$HOME'"
fi

????????關于腳本語言,我會在后續學習中更新,畢竟學會了命令而不會腳本語言有點缺失靈魂的感覺!!!

使用方法

  1. 將腳本保存為?compare_home_tilde.sh

  2. 賦予執行權限并運行:

    chmod +x compare_home_tilde.sh
    ./compare_home_tilde.sh

5.?注意事項

  • 引號的影響:

    • 雙引號內$HOME會被擴展,但~不會。例如:

      echo "~"      # 輸出 ~
      echo "$HOME"  # 輸出 /home/username

  • 腳本可移植性:

    • 在腳本中優先使用$HOME,因為其行為更明確;~的展開可能受Shell配置影響。

總結

? ? ~$HOME本質上是同一目標(用戶家目錄)的不同表示方式。理解它們的異同有助于在命令行或腳本中更靈活地操作路徑。


六、測試SHELL

????????在Linux操作系統中,我們輸入的各種命令需要通過命令行解釋器執行。Linux支持多種命令行解釋器(如bash、sh),可以通過查看環境變量SHELL來確認當前使用的解釋器類型。

echo $SHELL

該命令行解釋器實際上是系統內置的一條命令,當其運行成為進程后,便能提供命令行解釋功能。


七、環境變量相關命令

1、echo:顯示指定環境變量的值

2、export:設置新的環境變量

1?? 設置臨時環境變量

export MY_TEST="Hello World"

2?? 查看變量值

echo $MY_TEST

輸出:

3?? 驗證是環境變量(對子進程可見)

bash -c 'echo $MY_TEST'

輸出:

命令分解解釋

  1. bash:啟動一個新的 Bash 子 Shell(子進程)。

  2. -c:讓 Bash?執行后面跟著的命令字符串,而不是進入交互模式。

  3. 'echo $MY_TEST':在新啟動的 Bash 子進程中運行?echo $MY_TEST,即打印?MY_TEST?變量的值。

為什么用這個命令測試?export

  • 環境變量的核心特性
    使用?export?設置的變量會傳遞給子進程(如新啟動的 Shell、腳本或其他程序)。
    如果直接?echo $MY_TEST,只能證明變量在當前 Shell 存在,但無法確認它是否真的是環境變量(即是否能被子進程繼承)。

  • 這個測試的作用
    通過?bash -c?啟動一個全新的子 Shell,檢查?MY_TEST?是否能被子進程訪問:

    • 如果輸出?Hello World?→ 說明?export?成功(變量是環境變量)。

    • 如果無輸出 → 說明變量未被?export(只是當前 Shell 的局部變量)。

4?? 刪除環境變量(下面會介紹)

unset MY_TEST

5?? 驗證已刪除

echo $MY_TEST

(無輸出,變量已不存在)

3、env:顯示所有環境變量

環境變量說明

環境變量名稱作用描述
XDG_SESSION_ID當前用戶會話的唯一標識符,通常由系統管理工具(如 systemd)分配
HOSTNAME當前系統的主機名
TERM終端類型(如 xterm),用于控制終端的行為和顯示方式
SHELL當前用戶使用的默認 Shell 程序路徑(如 /bin/bash)
HISTSIZEShell 歷史記錄中保存的命令數量上限(如 10000)
SSH_CLIENTSSH 客戶端連接信息,格式為 IP 源端口 目標端口(如 183.20.145.59 61461 22)
SSH_TTYSSH 連接的終端設備文件路徑(如 /dev/pts/1)
USER當前登錄的用戶名(如 hmz)
LD_LIBRARY_PATH動態鏈接庫(.so 文件)的額外搜索路徑,優先級高于系統默認路徑
LS_COLORS定義 ls 命令輸出中不同文件類型的顯示顏色(如目錄、壓縮包、圖片等)
MAIL當前用戶的郵件存儲路徑(如 /var/spool/mail/hmz)
PATH系統查找可執行文件的路徑列表,用冒號分隔
PWD當前工作目錄的路徑(如 /home/hmz)
LANG系統語言和字符編碼設置(如 en_US.UTF-8)
HISTCONTROL控制 Shell 歷史記錄的存儲方式(如 ignoredups 表示忽略重復命令)
SHLVLShell 層級,表示當前 Shell 嵌套深度(初始為 1,每啟動一個子 Shell 加 1)
HOME當前用戶的主目錄路徑(如 /home/hmz)
LOGNAME登錄用戶名(通常與 USER 相同)
SSH_CONNECTIONSSH 連接的詳細信息,包括客戶端和服務端的 IP 及端口
LESSOPEN指定 less 命令的預處理腳本(如?/usr/bin/lesspipe.sh %s
XDG_RUNTIME_DIR用戶運行時文件(如套接字、PID 文件)的存儲目錄(如 /run/user/1000)
HISTTIMEFORMAT定義 Shell 歷史記錄中時間戳的格式(如 %F %T hmz 表示年-月-日 時:分:秒)

4、unset:刪除環境變量

1. 添加測試環境變量

export TEST_VAR="Hello, this is a test variable"

2. 驗證變量是否存在

echo $TEST_VAR

輸出:

3. 使用?unset?清除變量

unset TEST_VAR

4. 再次驗證變量

echo $TEST_VAR

輸出:(空,因為變量已被刪除)

5. 檢查是否從環境變量中移除

env | grep TEST_VAR

輸出:(空,表示環境變量列表中已不存在)

關鍵說明:

  • export?將變量提升為環境變量(對子進程可見)

  • unset?徹底刪除變量(無論是普通變量還是環境變量)

  • 測試時建議用新變量名(如?TEST_VAR),避免誤刪重要變量

5、set:顯示本地定義的Shell變量和環境變量

? ? set?命令不能直接添加環境變量,它的主要用途是?設置Shell內部選項?或?顯示所有變量(包括環境變量和局部變量)

1.?查看所有變量(快速測試)

# 設置一個普通變量
TEST_VAR="hello"# 用 set 查看變量(過濾顯示 TEST_VAR)
set | grep TEST_VAR

輸出:

2.?開啟/關閉調試模式

# 開啟調試(顯示每條執行的命令)
set -x# 執行一個命令(會看到詳細輸出)
echo "This is a test"# 關閉調試
set +x

輸出:

📌?總結

  • set?直接使用?→ 顯示所有變量(內容很多,建議用?grep?過濾)。

  • set -x?→ 調試腳本時顯示命令執行過程。

  • set +x?→ 關閉調試。


八、環境變量的組織方式

在系統當中,環境變量的組織方式如下:

????????每個程序都會接收一個環境變量表,該表是一個字符指針數組,每個指針指向一個以'\0'結尾的環境字符串,最后一個字符指針為空。


九、main函數的參數(超重點!!!)

????????在C和C++編程中,main函數是程序的入口點,它可以接收來自命令行的參數。main函數的參數通常有兩種形式:

1、無參數形式

int main(void) 
{// 程序代碼return 0;
}

這種形式表示?main?函數不接受任何參數。

2、帶參數形式

int main(int argc, char *argv[]) 
{// 程序代碼return 0;
}

或等價的:

int main(int argc, char **argv) 
{// 程序代碼return 0;
}

參數解析:

  1. argc(Argument Count)

    • 表示命令行參數的數量(包括程序名本身)。

    • 類型是?int

    • 例如:運行?./program arg1 arg2argc?的值為 3(./programarg1arg2)。

  2. argv(Argument Vector)

    • 是一個指向字符串數組的指針,存儲了所有的命令行參數。

    • argv[0]?是程序名稱(可能包含路徑)。

    • argv[1]?到?argv[argc-1]?是用戶輸入的參數。

    • argv[argc]?是?NULL(C標準保證)。

示例代碼:

#include <stdio.h>int main(int argc, char *argv[]) 
{printf("程序名稱: %s\n", argv[0]);printf("參數個數: %d\n", argc - 1);for (int i = 1; i < argc; i++) {printf("參數 %d: %s\n", i, argv[i]);}return 0;
}

運行示例:

假設編譯后的程序名為?demo,運行:

./demo hello world 123

輸出:

圖中的參數(argv?數組)是由?Shell 和操作系統?在程序啟動時自動切分的。具體過程如下:

1. 參數切分的參與者

  • Shell(命令行解釋器)
    當用戶在終端輸入命令(如?./demo hello world 123)時,(父進程)Shell 會按空格將字符串拆分成多個單詞(./demohelloworld123)。

  • 操作系統(內核)
    當 Shell 調用?execve()?等系統調用啟動程序時,內核會將拆分后的參數以?char* argv[]?數組的形式傳遞給新程序的入口(如?main?函數)。

2. 關鍵細節

  • 空格作為分隔符
    Shell 默認按空格切分參數。若參數包含空格,需用引號包裹(如?./demo "hello world")。

  • argv[0]?的特殊性
    通常是程序名(./demo),但可通過?execve()?修改(如符號鏈接調用時可能不同)。

  • NULL?結尾
    方便遍歷時確定參數列表的結束(類似 C 字符串的?\0)。

3、擴展:envp(環境變量,非標準)

某些編譯器(如GCC)支持第三個參數?envp,用于訪問環境變量:

#include<stdio.h>int main(int argc, char *argv[], char *envp[]) 
{int i = 0;// envp 存儲環境變量,以NULL結尾for (; envp[i] != NULL; i++) {printf("環境變量 %d: %s\n", i, envp[i]);}return 0;
}

這段代碼用于打印當前進程的所有環境變量:

但更可移植的方法是使用?getenv?函數(<stdlib.h>)。(后面會講解)

注意事項:

  1. 參數順序argc?必須在?argv?之前。

  2. 參數名稱argc?和?argv?是約定俗成的命名,但可以自定義(如?int main(int count, char **args))。

  3. 返回值:通常返回?0?表示成功,非零值表示錯誤(由操作系統或調用者處理)。

通過?main?函數的參數,程序可以靈活地接收外部輸入,實現命令行交互功能。

4、編寫命令選項選擇

我們可以編寫一段簡單的代碼,根據用戶選擇的選項顯示不同的提示信息:

#include <stdio.h>                                                                                                                         
#include <string.h>int main(int argc, char *argv[], char* envp[])
{if(argc > 1){if(strcmp(argv[1], "-a") == 0){printf("you used -a option...\n");}else if(strcmp(argv[1], "-b") == 0){printf("you used -b option...\n");}else{printf("you used unrecognizable option...\n");}}else{printf("you did not use any option...\n");}return 0;
}

代碼運行結果如下:?

由此,我們可以得到一個結論:

指令選項的實現原理:main的命令行參數,是實現程序不同子功能的方法!!!


十、_start函數與main函數的關系

在C/C++程序中,_start?和?main?函數的關系是程序執行流程的關鍵環節。

1、_start:程序的真正入口

  • 系統級入口:當操作系統加載程序后,首先執行的是?_start(由C運行時庫或鏈接器默認提供),而非直接跳轉到?main。它是二進制程序的實際入口點(可通過鏈接腳本或編譯器選項修改)。

  • 職責

    • 初始化全局變量、堆棧、內存管理等運行時環境。

    • 設置命令行參數(argc,?argv)和環境變量。

    • 調用?main?函數,并將參數傳遞給它。

2、main:開發者定義的入口

  • 用戶級入口main?是程序員編寫的程序邏輯起點,但它的調用是由?_start?發起的。

  • 執行流程
    _start?→ 初始化代碼 →?main(argc, argv, envp)?→ 程序邏輯 → 返回到?_start?→ 調用?exit?結束進程。

3、關鍵區別

特性_startmain
定義者編譯器/運行時庫(如glibc的crt0開發者
可見性隱藏(通常無需手動實現)必須顯式定義
返回值直接調用系統退出(如exit返回值由?_start?傳遞給系統

4、示例流程

// 1. _start (隱藏的初始化)
void _start() {// 初始化環境int argc = ...;char **argv = ...;// 調用mainint ret = main(argc, argv);// 退出程序exit(ret);
}// 2. 用戶編寫的main
int main(int argc, char **argv) {printf("Hello, World!\n");return 0;
}

5. 特殊情況

  • 嵌入式系統:可能直接由匯編代碼跳轉到?main,跳過部分初始化。

  • 繞過main:可通過修改鏈接參數指定其他入口函數(如?_start?調用?my_main?而非?main)。

6、總結

? ? _start?是系統與用戶代碼之間的橋梁,負責準備執行環境并啟動?mainmain?則是開發者控制程序邏輯的入口。兩者共同構成程序的完整生命周期。


十一、通過代碼獲取環境變量

方法一:使用命令行第三個參數

#include <stdio.h>int main(int argc, char *argv[], char *env[]) 
{for(int i = 0; env[i]; i++) {printf("%s\n", env[i]);}return 0;
}

方法二:通過第三方變量environ獲取

#include <stdio.h>int main(int argc, char *argv[]) 
{extern char **environ;int i = 0;for(; environ[i]; i++) {printf("%s\n", environ[i]);}return 0;
}

注意:

????????libc中定義的全局變量environ指向環境變量表。由于environ未包含在任何頭文件中,使用時需要用extern聲明。


十二、通過系統調用獲取或設置環境變量

????????除了通過main函數的第三個參數和environ全局變量獲取環境變量外,系統還提供了getenv函數來查詢環境變量。

getenv?和?putenv?函數解析

????????在 C 語言中,getenv?和?putenv?是用于操作環境變量的兩個標準庫函數(定義在?<stdlib.h>?中)。它們的作用不同,但都與環境變量的讀取和修改相關。

1.?getenv?函數(獲取環境變量)

函數原型

char *getenv(const char *name);

功能

  • 用于獲取指定名稱(name)的環境變量的值。

  • 如果環境變量存在,返回其值的字符串指針(char*)。

  • 如果環境變量不存在,返回?NULL

示例

#include <stdio.h>
#include <stdlib.h>int main() 
{const char *path = getenv("PATH");if (path != NULL) {printf("PATH 的值是: %s\n", path);} else {printf("PATH 未設置\n");}return 0;
}

輸出示例

注意事項

  • 返回的字符串是只讀的,不能直接修改(否則可能導致未定義行為)。

  • 如果環境變量不存在,getenv?返回?NULL,因此使用前應檢查返回值。

2.?putenv?函數(設置/修改環境變量)

函數原型

int putenv(char *string);

功能

  • 用于設置或修改環境變量。

  • 參數?string?必須是?"NAME=value"?形式的字符串。

  • 成功返回?0,失敗返回非?0(具體實現可能不同)。

示例

#include <stdio.h>
#include <stdlib.h>int main() 
{// 設置或修改環境變量if (putenv("MY_VAR=HelloWorld") != 0) {perror("putenv 失敗");return 1;}// 使用 getenv 獲取新設置的環境變量const char *my_var = getenv("MY_VAR");if (my_var != NULL) {printf("MY_VAR 的值是: %s\n", my_var);} else {printf("MY_VAR 未設置\n");}return 0;
}

輸出示例

注意事項

  • putenv?的參數格式必須是?"NAME=value",否則可能不會生效。

  • 修改后的環境變量會影響當前進程及其子進程,但不會影響父進程(如 Shell)。

  • 在 Windows 上,類似的函數是?_putenv(兼容性問題)。

3. 主要區別

函數作用參數格式返回值是否修改環境變量
getenv獲取環境變量的值"VAR_NAME"char*(值)或?NULL? 只讀
putenv設置/修改環境變量"VAR_NAME=value"0(成功)或非?0(失敗)? 修改

4. 綜合示例

#include <stdio.h>
#include <stdlib.h>int main() 
{// 獲取當前 PATHconst char *old_path = getenv("PATH");printf("原 PATH: %s\n", old_path);// 修改 PATH(追加一個新路徑)putenv("PATH=/new/path:/usr/local/bin");// 再次獲取 PATHconst char *new_path = getenv("PATH");printf("新 PATH: %s\n", new_path);return 0;
}

輸出示例

5. 安全注意事項

  1. getenv?返回值不可修改

    char *path = getenv("PATH");
    // path[0] = 'X';  // 錯誤!可能導致程序崩潰
  2. putenv?的字符串必須是持久存儲的

    char env_str[] = "TMP_VAR=123";
    putenv(env_str);  // 可以
    // 但如果 env_str 是局部變量,函數返回后可能失效
  3. 跨平臺兼容性

    • Windows 使用?_putenv?而不是?putenv(需?#include <stdlib.h>)。

    • 更安全的替代方案:setenv?和?unsetenv(POSIX 標準,但 Windows 不支持)。

6. 替代方案(POSIX)

如果運行在 Linux/macOS(POSIX 環境),可以使用更安全的:

  • setenv("NAME", "value", 1):設置環境變量(1?表示覆蓋,0?表示不覆蓋)。

  • unsetenv("NAME"):刪除環境變量。

示例

#include <stdlib.h>
#include <stdio.h>int main() 
{setenv("MY_VAR", "Hello", 1);  // 設置或覆蓋printf("MY_VAR = %s\n", getenv("MY_VAR"));unsetenv("MY_VAR");  // 刪除printf("MY_VAR = %s\n", getenv("MY_VAR") ? getenv("MY_VAR") : "(null)");return 0;
}

輸出

7、總結

操作推薦函數說明
獲取環境變量getenv返回?char*,需檢查?NULL
設置/修改環境變量putenv(Windows:?_putenv格式?"NAME=value"
更安全的設置(POSIX)setenv可控制是否覆蓋
刪除環境變量(POSIX)unsetenvWindows 無直接等效函數

十三、環境變量通常具備全局屬性

1、核心知識點

環境變量的全局特性

  • 定義:環境變量通過?export?導出后,具備全局性,可被當前Shell及其所有子進程(如運行的腳本、編譯的程序)繼承。

  • 關鍵規則

    • 子進程會復制父進程的環境變量表(environ),但無法反向修改父進程的環境。

環境變量具有全局特性,可以被子進程繼承,通過下面的代碼編譯出來的程序可以驗證:

#include <stdio.h>
#include <stdlib.h>int main() 
{char *env = getenv("MYENV");if(env) {printf("%s\n", env);}return 0;
}

(1) 初次運行程序無輸出

原因:未通過?export?設置?MYENV,該環境變量不存在于當前Shell及其子進程的環境中。

(2) 導出環境變量

export MYENV="hello world"  # 設置為全局環境變量

作用MYENV?被添加到Shell的環境變量表中,后續所有子進程均可繼承。

(3) 再次運行程序輸出結果

原因:子進程(a.out)繼承了父Shell(Bash)的環境變量表,getenv("MYENV")?成功讀取到值。

2、思考現象背后的原理:為什么會出現這種現象?

進程環境變量繼承機制

  • 父進程傳遞:當父進程(如Bash)調用子進程(如C程序)時,會將自己的環境變量表(鍵值對數組)復制給子進程。

  • C程序獲取方式

    • getenv("VAR"):從子進程的環境變量表中查找指定變量。

    • extern char **environ:直接訪問環境變量表(全局變量)。

為什么需要?export

????????普通變量(未?export)僅存在于Shell進程內部,不會寫入環境變量表,因此子進程無法通過?getenv()?獲取。


十四、環境變量和本地變量

????????在Bash中,變量分為環境變量本地變量(也稱為Shell變量),它們的核心區別在于作用域是否被子進程繼承。以下是詳細對比:

1、作用范圍

類型作用域是否全局
環境變量當前Shell及其所有子進程全局有效(跨進程)
本地變量僅當前Shell進程內部局部有效(不跨進程)

2、定義與導出方式

環境變量

  • 通過?export?命令導出,子進程(如腳本、其他程序)可繼承:

    export MY_ENV_VAR="value"  # 定義為環境變量
  • 或分兩步:

    MY_ENV_VAR="value"
    export MY_ENV_VAR

本地變量

  • 直接賦值,僅當前Shell可用:

    MY_LOCAL_VAR="local_value"  # 定義為本地變量

3、子進程繼承性

類型子進程是否可見示例驗證
環境變量? 是bash -c 'echo $MY_ENV_VAR'
本地變量? 否bash -c 'echo $MY_LOCAL_VAR'

示例

export ENV_VAR="global"
LOCAL_VAR="local"# 子進程(新bash)測試
bash -c 'echo "子進程環境變量: $ENV_VAR"; echo "子進程本地變量: $LOCAL_VAR"'

輸出

4、常見用途

  • 環境變量

    • 配置程序運行環境(如?PATH,?HOME)。

    • 在腳本間傳遞參數。

  • 本地變量

    • 臨時存儲腳本內部使用的數據。

    • 避免污染子進程環境(如循環計數器)。

5、查看與刪除

  • 查看所有變量

    set          # 顯示所有變量(包括本地和環境)
    env          # 僅顯示環境變量
  • 刪除變量

    unset MY_VAR  # 刪除變量(無論環境或本地)

6、擴展知識

  • 持久化環境變量
    若需永久生效,需寫入Shell配置文件(如?~/.bashrc?或?~/.profile)。

  • 函數中的局部變量
    在函數內使用?local?聲明變量,作用域僅限于函數:

    func() {local LOCAL_IN_FUNC="secret"echo $LOCAL_IN_FUNC
    }

7、總結

  • 環境變量是“全局護照”,子進程可繼承;本地變量是“臨時便簽”,僅限當前Shell。

  • 通過?export?控制變量的可見性,合理使用兩者能有效管理腳本的執行環境。


十五、關于內置命令的必知知識

????????在 Shell 中,內置命令(Built-in Commands)是直接集成在 Shell 解釋器(如 Bash、Zsh 等)中的命令,無需調用外部程序或創建子進程即可執行。

1、內置命令的特點

  1. 不創建子進程
    直接由當前 Shell 進程執行,效率更高(例如?cd?需要改變當前 Shell 的工作目錄,若通過子進程執行則無法生效)

  2. 操作本地變量和環境變量
    可以直接讀取或修改當前 Shell 的變量(如?set,?export,?read)。

  3. 無需外部二進制文件
    不依賴?PATH?環境變量,即使系統文件損壞也能使用。

  4. 優先級高于外部命令
    當內置命令與外部命令同名時,優先執行內置命令(可通過?command?或完整路徑調用外部命令)。

2、常見內置命令分類

1. 基礎操作

命令功能描述示例
cd切換工作目錄(必須內置,否則無法影響當前 Shell)cd /tmp
echo輸出字符串(支持?-e?解析轉義符等特性)echo -e "Line1\nLine2"
pwd打印當前目錄(內置版本可避免符號鏈接問題)pwd -P(顯示物理路徑)
read從標準輸入讀取數據并賦值給變量read -p "Name: " username

2. 變量管理

命令功能描述示例
set設置/顯示 Shell 變量和選項(如?set -x?開啟調試模式)set -o vi(啟用 vi 編輯模式)
unset刪除變量或函數unset PATH
export將變量提升為環境變量(供子進程使用)export VAR=value
local在函數內定義局部變量(僅限函數作用域)local var="temp"

3. 流程控制

命令功能描述示例
exit退出當前 Shell 或腳本(可指定退出狀態碼)exit 1(異常退出)
return從函數中返回(可帶狀態碼)return 0(函數成功返回)
source/.在當前 Shell 執行腳本(不創建子進程,影響當前環境)source ~/.bashrc
alias定義命令別名alias ll='ls -l'
unalias刪除別名unalias ll

4. 作業控制

命令功能描述示例
jobs查看后臺運行的作業列表jobs -l(顯示 PID)
fg將后臺作業切換到前臺繼續運行fg %1(恢復作業 1)
bg將暫停的作業放到后臺運行bg %2(后臺運行作業 2)
wait等待指定作業或所有后臺作業完成wait %1(等待作業 1 完成)

3、如何判斷命令是否為內置?

使用?type?命令檢測:

type cd      # 輸出: cd is a shell builtin
type ls      # 輸出: ls is /usr/bin/ls (外部命令)

4、與外部命令的區別

特性內置命令外部命令
執行方式由 Shell 直接解釋需創建子進程,通過?PATH?查找二進制文件
變量操作可修改當前 Shell 環境僅繼承環境變量,無法修改父 Shell 變量
速度更快(無進程創建開銷)相對較慢
依賴項不依賴外部文件依賴系統二進制文件

5、注意事項

  1. 特殊的內置命令
    某些命令既有內置實現也有外部實現(如?echoprintf)。內置版本可能支持擴展功能,而外部版本更符合 POSIX 標準。強制調用外部版本:

    /bin/echo "Hello"  # 顯式調用外部命令
    command echo "Hello"  # 繞過別名和內置命令
  2. 腳本可移植性
    內置命令的行為可能因 Shell 不同(如 Bash 和 Dash)而有差異,需測試兼容性。

  3. 性能敏感場景
    在循環中優先使用內置命令(如?(( i++ ))?替代?expr i + 1)。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/89032.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/89032.shtml
英文地址,請注明出處:http://en.pswp.cn/web/89032.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Spring Boot:DTO 字段 cPlanId 無法反序列化的奇葩問題

本文記錄一次在 Spring Boot 項目中&#xff0c;DTO 字段明明有值&#xff0c;反序列化后卻是 null 的問題。最終發現并不是常見的 JSON 工具庫 Bug&#xff0c;而是隱藏在 setter 命名大小寫規則中的坑。&#x1f4bb; 背景介紹技術棧如下&#xff1a;Spring Boot&#xff1a;…

文本生成視頻的主要開源模型

AI文本到視頻生成技術發展迅速&#xff0c;這些模型的“快速”通常指相對于傳統視頻制作的效率&#xff08;生成時間從幾秒到幾分鐘&#xff0c;取決于硬件&#xff09;&#xff0c;但實際速度取決于您的計算資源&#xff08;如GPU&#xff09;。這些模型大多依賴于深度學習框架…

vscode里面怎么配置ssh步驟

01.ubuntu里面下載幾個插件還需要下載插件net-tools02.vscode里面下載插件會生成下面類似電視機的插件(room6)

【人工智能99問】激活函數有哪些,如何選擇使用哪個激活函數?(5/99)

文章目錄激活函數一、激活函數的分類1. 按“是否線性”分類2. 按“是否飽和”分類&#xff08;針對非線性激活函數&#xff09;3. 按“適用層”分類二、常見激活函數及特點&#xff08;一&#xff09;非線性激活函數&#xff08;主要用于隱藏層&#xff09;1. 飽和激活函數&…

代數——第4章——線性算子(算符)(Michael Artin)

第 4 章 線性算子(Linear Operators) That confusions of thought and errors of reasoning still darken the beginnings of Algebra, is the earnest and just complaint of sober and thoughtful men. (思維混亂和推理錯誤 仍然使代數的開端變得模糊不清&#xff0c; …

Neo4j Python 驅動庫完整教程(帶輸入輸出示例)

Neo4j Python 驅動庫完整教程&#xff08;帶輸入輸出示例&#xff09; 1. 基礎連接示例 輸入代碼 from neo4j import GraphDatabase# 連接配置 URI "bolt://localhost:7687" USER "neo4j" PASSWORD "password123" # 替換為你的實際密碼def t…

Axios 和 Promise 區別對比

Axios 和 Promise 是前端開發中兩個不同的概念&#xff0c;盡管 Axios 基于 Promise 實現&#xff0c;但它們的核心定位和功能有顯著區別。以下是對比分析&#xff1a; 1. 核心定位與功能Promise 定義&#xff1a;Promise 是 JavaScript 的異步編程方案&#xff0c;用于處理異步…

Git分支管理與工作流詳解

前言 分支管理是Git最強大的功能之一&#xff0c;它允許開發者在不影響主代碼庫的情況下創建獨立的工作空間。本文將詳細介紹Git分支的操作和常見工作流策略&#xff0c;幫助團隊更高效地協作開發。 1. Git分支的基本概念 1.1 什么是分支 在Git中&#xff0c;分支本質上是指…

【flutter】flutter網易云信令 + im + 聲網rtm從0實現通話視頻文字聊天的踩坑

接了一個國外的項目,項目采用網易云im 網易云信令聲網rtm遇到的一些問題這個項目只對接口,給的工期是兩周,延了工期,問題還是比較多的 需要全局監聽rtm信息,收到監聽內容,引起視頻通話網易云給的文檔太爛,所有的類型推策只能文檔一點點推聲網的rtm配置網易云的信令,坑太多,比如…

hive/spark sql中unix_timestamp 函數的坑以及時間戳相關的轉換

我用的是hive版本是3.1.3&#xff0c;spark版本是3.3.1&#xff0c;它們的unix_timestamp 函數在同樣的語句下轉換出來的時間戳是完全不同的&#xff0c;如下試驗所示1.unix_timestamp 函數的坑上圖試驗中我同樣的計算 2025-07-11 10:00:00 時間點對應的時間戳&#xff0c;但是…

MyBatis專欄介紹

專欄導讀 在當今的軟件開發領域&#xff0c;持久層框架的選擇對于提高開發效率和數據處理能力至關重要。MyBatis作為一個半自動化的ORM框架&#xff0c;因其靈活、高效的特點&#xff0c;在眾多開發者中廣受好評。本專欄《MyBatis實戰》旨在通過深入淺出的方式&#xff0c;幫助…

HarmonyOS從入門到精通:自定義組件開發指南(七):自定義事件與回調

HarmonyOS從入門到精通&#xff1a;自定義組件開發指南&#xff08;七&#xff09;&#xff1a;自定義事件與回調 在HarmonyOS應用開發中&#xff0c;組件化架構是構建復雜界面的基礎&#xff0c;而組件間的高效通信則是實現業務邏輯的核心。自定義事件與回調機制作為組件交互的…

C++編程學習(第七天)

基于過程的程序設計C既可以用來進行基于過程的程序設計&#xff0c;又可以用來進行面向對象的程序設計。基于過程的程序設計又稱為過程化的程序設計&#xff0c;它的特點是&#xff1a;程序必須告訴計算機應當具體怎么做&#xff0c;也就是要給出計算機全部操作的具體過程&…

ubuntu透網方案

場景&#xff1a;兩個linux/Ubuntu系統&#xff0c;一個可以上網&#xff0c;一個不能&#xff0c;讓不能上網的,讓能上網的共享網絡 步驟 1&#xff1a;修改 /etc/sysctl.conf sudo nano /etc/sysctl.conf 找到或添加以下行&#xff1a; net.ipv4.ip_forward1 按 CtrlO 保存&a…

基于Python的物聯網崗位爬取與可視化系統的設計與實現【海量數據、全網崗位可換】

文章目錄有需要本項目的代碼或文檔以及全部資源&#xff0c;或者部署調試可以私信博主項目介紹數據采集數據預處理系統展示總結每文一語有需要本項目的代碼或文檔以及全部資源&#xff0c;或者部署調試可以私信博主 項目介紹 隨著物聯網技術的迅速發展&#xff0c;物聯網行業…

線性回歸原理推導與應用(十):邏輯回歸多分類實戰

本篇文章將利用sklearn中內置的鳶尾花數據進行邏輯回歸建模并對鳶尾花進行分類。對于邏輯回歸和線性回歸的相關原理&#xff0c;可以查看之前的文章 數據導入 鳶尾花數據是機器學習里的常用數據&#xff0c;首先導入一些基礎庫并從sklearn中導入數據集 #導入用到的一些pytho…

Docker 部署emberstack/sftp 鏡像

Docker 部署 emberstack/sftp 鏡像 1、找到國內可用的docker源,本次測試使用docker.1ms.run 2、下載emberstack/sftp鏡像docker pull docker.1ms.run/emberstack/sftp3、安裝并啟動emberstack/sftp鏡像docker run -d -p 22:22 --name SFTP -v D:\SFTP:/home/sftpuser/sftp --pr…

【華為OD】MVP爭奪戰2(C++、Java、Python)

文章目錄題目題目描述輸入描述輸出描述示例思路核心思路&#xff1a;關鍵觀察&#xff1a;算法步驟&#xff1a;排序策略&#xff1a;特殊情況處理&#xff1a;代碼CJavaPython復雜度分析時間復雜度空間復雜度結果總結題目 題目描述 給定一個整型數組&#xff0c;請從該數組中…

Python打卡訓練營Day58

DAY 58 經典時序預測模型2知識點回顧&#xff1a;時序建模的流程時序任務經典單變量數據集ARIMA&#xff08;p&#xff0c;d&#xff0c;q&#xff09;模型實戰SARIMA摘要圖的理解處理不平穩的2種差分n階差分---處理趨勢季節性差分---處理季節性建立一個ARIMA模型&#xff0c;通…

003大模型基礎知識

大模型分類&#xff1a; 技術架構&#xff1a; Encoder Only Bert Decoder Only 著名的大模型都是 Encoder - Decoder T5 是否開源&#xff1a; 開源陣營&#xff1a; Llama DeepSeek Qwen 閉源陣營&#xff1a; ChatGpt Gemini Claude 語言模型發展階段&am…