目錄
Shell的變量功能
什么是變量
變數的可變性與方便性
影響bash環境操作的變量
腳本程序設計(shell script)的好幫手
變量的使用:echo
變量的使用:HOME?
環境變量相關命令
獲取環境變量?
環境變量和本地變量?
命令行參數?
什么是命令行參數
命令行參數有什么用?
環境變量與命令行參數?
Shell的變量功能
什么是變量
變數的可變性與方便性
影響bash環境操作的變量
腳本程序設計(shell script)的好幫手
變量的使用:echo
[root@www ~]# echo $variable
[root@www ~]# echo $PATH
/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin
[root@www ~]# echo ${PATH}----------------------------------------------------------------------[root@www ~]# echo $myname<==這里幵沒有任何數據~因為這個變量尚未被設置!是空的!
[root@www ~]# myname=VBird
[root@www ~]# echo $myname
VBird <==出現了!因為這個發量已經被設定了!
?若該變量需要擴增變量內容時,則可用 "$變量名稱" 戒 ${變量} 累加內容,如下所示:
『PATH="$PATH":/home/bin』
我們知道執行一個程序時,是需要./mytest 的,但下面這樣設置之后,直接mytest,不用帶 ./ ,當前路徑該程序就可以直接運行。
- 方法 1:把 proc 拷貝到環境變量 PATH 指定的任一路徑下。
- 方法 2:把 proc 所在路徑添加到環境變量 PATH 中。(注意:自己添加的環境變量,系統重啟后是不會被保存的)
$ PATH=$PATH:/home/ll/xxx/10# :冒號是分隔符 # 注意:PATH=/home/ll/xxx/10,不能這樣寫,必須加上$符號,否則會把PATH中所有內容覆蓋掉
?效果圖:
變量的使用:HOME?
????????分別在 root 和普通用戶下執行 cd ~ 和 pwd 查看家目錄,分別是 /root?和 /home/test,為什么得到的結果不一樣呢?因為不同用戶的家目錄中的配置文件不一樣,所以不同用戶下的環境變量 HOME 也是不一樣的。
[root@VM-0-12-centos ~]$ echo $HOME
/root
[test@VM-0-12-centos ~]$ echo $HOME # 指定用戶的主工作目錄
/home/test
環境變量相關命令
- echo:顯示某個環境變量值。
- export:設置一個新的環境變量。
- env:顯示所有環境變量。
- unset:清除環境變量。
- set:顯示本地定義的 shell 變量和環境變量。
獲取環境變量?
getenv?和?setenv?函數介紹:
#include <stdlib.h>char *getenv(const char *name); // 獲取環境變量
int setenv(const char *name, const char *value, int overwrite); // 更改或添加環境變量name:環境變量的名稱
返回值:環境變量的內容
// proc.c
#include <stdio.h>
#include <stdlib.h> // getenvint main()
{printf("%s\n", getenv("PATH"));printf("%s\n", getenv("HOME"));printf("%s\n", getenv("SHELL"));return 0;
}
代碼通過?getenv
?函數讀取了?PATH
、HOME
、SHELL
?等環境變量。這些變量的值繼承自父進程(即啟動該程序的進程)。
- 在命令行運行?
./proc.c
?時,父進程是 bash。 - bash 在啟動時會加載自己的環境變量(例如從?
~/.bashrc
),并在創建子進程(你的程序)時將這些變量傳遞給子進程。
當你在 bash 中輸入命令時(如?ls
?或?./a.out
),bash 的典型行為是:
- fork():創建一個子進程(復制父進程 bash 的環境變量、內存狀態等)。
- exec():在子進程中替換為新的程序(如?
ls
?或?./a.out
)。 - 環境變量繼承:子進程通過繼承機制獲得父進程(bash)的環境變量。
因此,你的程序中通過?getenv
?讀取到的環境變量,實際上是父進程 bash 傳遞下來的。那么就很好的引出了環境變量和本地變量的區別,具體請看如下。
環境變量和本地變量?
環境變量(環境變量通常具有全局屬性:可以被子進程繼承下去)。環境變量實際上是在當前 bash 的上下文中定義的。所以在當前命令行解釋器 bash 內可以被訪問到,在 bash 創建的子進程內也可以被訪問到。
#include <stdio.h>
#include <stdlib.h>int main()
{char* env = getenv("MYENV");if(env){printf("%s\n", env);}return 0;
}
直接運行發現沒有結果,說明該環境變量根本不存在。
- 導出環境變量:export MYENV="hello world"
- 再次運行程序,發現有結果了。說明:環境變量是可以被子進程繼承下去的。
本地變量(只能在當前 shell 命令行解釋器內被訪問,不可以被子進程繼承)
[ll@VM-0-12-centos 10]$ MY_VAL="hello world" # 定義本地變量(在bash內定義的)
?如何查看本地變量呢?
[ll@VM-0-12-centos 10]$ echo $MY_VAL # 在當前命令行解釋器bash內訪問本地變量
hello world
如何證明本地變量不能被子進程繼承??
// proc.c
#include <stdio.h>
#include <stdlib.h> // getenvint main()
{printf("%s\n", getenv("MY_VAL"));return 0;
}
該怎么解決這個問題呢?可以將本地變量?MY_VAL 設置成環境變量。
[ll@VM-0-12-centos 10]$ exprot MY_VAL # 把本地變量MY_VAL導出成環境變量
上面說到,我們在命令行上運行的大部分命令,都是 bash 創建子進程來執行的,而本地變量不能被子進程繼承,那為什么使用 echo 命令,卻可以訪問本地變量呢??
在Linux中的命令分類:
# 將PATH變量設置為空
[test@VM-16-11-centos ~]$ export PATH=""
# 以下命令均不能使用
[test@VM-16-11-centos ~]$ ll
-bash: ls: No such file or directory
[test@VM-16-11-centos ~]$ touch
-bash: touch: No such file or directory
[test@VM-16-11-centos ~]$ mkdir
-bash: mkdir: No such file or directory
# pwd仍然可以使用
[test@VM-16-11-centos ~]$ pwd
/home/test
- 常規命令:shell通過fork讓子進程執行的
- 內建命令:shell命令行的一個函數,可以直接讀取shell內部定義的本地變量
而在上面的測試中,像ls,mkdir這樣的命令,都是shell通過fork創建子進程來執行的,而這里的PATH路徑已經被用戶破壞了,因此找不到搜索的路徑,因此找不見是當然的事,但是為什么pwd可以找到?這是由于pwd這樣的命令是內建命令,這是shell命令行的一個函數,可以直接讀取shell內部的本地變量,因此就可以找到對應的值進行輸出了。
會提供一個environ
指針,這個指針會指向一張環境表,環境表是一個字符指針數組,每一個指針都會有一個字符串。
命令行參數?
什么是命令行參數
在學習C
語言中,對于main
函數當初的寫法是沒有任何參數的,但是實際上,main
函數是可以有參數的,比如下面的寫法:
#include <stdio.h>int main(int argc, char* argv[])
{int i = 0;for (i = 0; i < argc; i++){printf("%d:%s\n", i, argv[i]);}return 0;
}
運行結果:
[test@VM-16-11-centos 10_15]$ vim myproc.c
[test@VM-16-11-centos 10_15]$ make
gcc -o myproc myproc.c
[test@VM-16-11-centos 10_15]$ ./myproc
0:./myproc
其實,命令行參數是用來支持各種指令級別的命令行選項的設置,例如這里的argv
數組中,存儲的字符串其實就是用戶寫的命令,而如果現在我在運行程序的時候帶上選項 :
[test@VM-16-11-centos 10_15]$ ./myproc -a -b -c -d
0:./myproc
1:-a
2:-b
3:-c
4:-d
所以推出:?
int main(int argc, char *argv[]) {// argc 表示參數總數(包括程序名本身)// argv[0] 是程序名,argv[1] 是第一個參數,依此類推
}
????????并且運行結果發現打印的信息就是以用戶寫的指令開始進行分割,然后把信息放到數組中,從中其實看出,用戶在運行程序的時候寫的命令或者是帶的選項都可以被main函數讀取并且傳參,函數體內部就可以利用這個機制實現不同的選項帶來的結果。比如,Linux中有ls命令,用來查看文件夾中的內容,如果使用的是ls -a或者是ls -l這些選項,就會產生不同的結果,實際上這樣的結果也是通過這個原理,通過讀取argv數組中的內容就可以實現各種目的。?
示例:ls
?命令的選項實現邏輯
// 偽代碼示意
#include<stdio.h>
#define SHOW_HIDDEN (1 << 0) // 二進制 0b01
#define LONG_FORMAT (1 << 1) // 二進制 0b10int main(int argc, char *argv[]) {int options = 0; // 初始化選項標志位for (int i = 1; i < argc; i++) {if (strcmp(argv[i], "-a") == 0) {options |= SHOW_HIDDEN; // 設置顯示隱藏文件標志} else if (strcmp(argv[i], "-l") == 0) {options |= LONG_FORMAT; // 設置長格式輸出標志}}// 根據 options 執行相應邏輯list_directory(".", options);return 0;
}
$ ./program
運行結果:file1.txt file2.txt dir1$ ./program -a
運行結果:. .. .hidden_file file1.txt file2.txt dir1$ ./program -l
運行結果:
-rw-r--r-- 1 user group 1024 Jan 1 10:00 file1.txt
drwxr-xr-x 2 user group 4096 Jan 1 10:00 dir1$ ./program -a -l
運行結果:
drwxr-xr-x 2 user group 4096 Jan 1 10:00 .
drwxr-xr-x 5 user group 4096 Jan 1 10:00 ..
-rw-r--r-- 1 user group 0 Jan 1 10:00 .hidden_file
-rw-r--r-- 1 user group 1024 Jan 1 10:00 file1.txt
drwxr-xr-x 2 user group 4096 Jan 1 10:00 dir1$ ./program -x
運行結果:
file1.txt file2.txt dir1
- 無參數:默認行為(不顯示隱藏文件,短格式)。
-a
:顯示隱藏文件。-l
:長格式顯示文件元數據。-a -l
:同時啟用兩種模式。- 無效參數:被忽略,按默認模式運行。
命令行參數有什么用?
為什么要存在命令行參數呢?
幫助我們能夠給同一個程序,設計出不同的業務功能。
舉個小例子,比如我想要實現這樣一個計算器:
如果輸入 ./cal,則會提示該程序的正確用法:Usage:./cal -[a|s] x y;輸入 ./cal -a 1 2,cal 程序可以輸出 1 + 2 的結果;
輸入 ./cal -s 4 2,cal 程序可以輸出 4 - 2 的結果。
#include <stdio.h>
#include <stdlib.h> // atoi -- 函數原型:int atoi(const char *nptr); // 將C字符串轉換成整數
#include <string.h> // strcmp// cal命令的用法手冊
void Usage(const char* cal)
{printf("Usage: %s -[a|s] x y\n", cal);
}int main(int argc, char* argv[]) // 接收命令行參數
{// 輸入的參數個數不為4if (argc != 4){Usage(argv[0]);return 1; // 退出程序}// 保存第3個和第4個參數 int x = atoi(argv[2]);int y = atoi(argv[3]);// 根據不同參數,執行不同功能,然后輸出結果if (strcmp(argv[1], "-a") == 0){ printf("%d + %d = %d\n", x, y, x + y); }else if (strcmp(argv[1], "-s") == 0){printf("%d - %d = %d\n", x, y, x - y); }else{Usage(argv[0]);return 1; // 退出程序}return 0;
}
運行結果:?
[test@VM-0-12-centos 10]$ ./cal # 命令使用手冊
Usage: ./cal -[a|s] x y
[test@VM-0-12-centos 10]$ ./cal -a 1 2 # 實現加法
1 + 2 = 3
[test@VM-0-12-centos 10]$ ./cal -s 4 2 # 實現減法
4 - 2 = 2
環境變量與命令行參數?
main 函數除了可以傳遞兩個和命令行參數相關的參數 argc 和 argv 以外,還可以傳遞第 3 個參數?env:
int main(int argc, char* argv[], char* env[]);
這也是 main 函數獲取環境變量的方式。通過給 main 函數第三個參數傳參,把一個個環境變量傳遞給當前程序,當前程序運行起來變成進程,就意味著當前這個進程獲取到了這些環境變量。
每個被 bash 創建的子進程都會接收到一張環境表,環境表是一個字符指針數組,每個指針指向一個以?’\0’?結尾的環境字符串(環境變量)。
// proc.c
#include <stdio.h>
#include <string.h>int main(int argc, char* argv[], char* env[]) // 通過第三個參數接收環境變量
{for (int i = 0; env[i]; i++) // 循環結束條件為env[i],遍歷到NULL停止{printf("env[%d]: %s\n", i, env[i]); // 遍歷字符指針數組env}return 0;
}
運行結果:獲取到了當前 bash 內的所有環境變量。(因為環境變量被 bash 創建的子進程 proc 繼承下去了)
所以現在知道 C 庫函數 getenv 的實現原理了,比如?printf("%s\n", getenv("PATH"));,其實就是通過在字符指針數組 env 中進行字符匹配,找到 "PATH" 后面的內容并返回。?