目錄
一、Shell 是什么
二、?.sh?腳本調用?.py?腳本
Python 核心邏輯腳本(data_processor.py)
Shell 腳本(pipeline.sh)
三、常見命令
四、.sh腳本
1. 簡單例子
2. 進階例子
3. 猜數字游戲
一、Shell 是什么
Shell 的本質是?“命令行解釋器(Command-Line Interpreter)”,其核心功能是:
- 接收用戶輸入:用戶在終端(Terminal)中輸入命令(如?
ls
?查看文件、python train.sh
?啟動訓練腳本); - 解析命令含義:Shell 把用戶輸入的 “人類可讀命令” 翻譯成內核能理解的 “機器指令”;
- 調用內核執行:Shell 將解析后的指令傳遞給內核,讓內核調度硬件完成任務(如讀取磁盤文件、分配 GPU 資源);
- 返回執行結果:內核執行完任務后,將結果通過 Shell 反饋給用戶(如在終端顯示文件列表、訓練日志)。
簡言之,如果把?操作系統內核(如 Linux 內核、Windows 內核)?看作 “電腦的發動機”(負責硬件調度、資源分配等核心工作),那么?Shell?就是 “發動機的操作面板”—— 它是用戶與操作系統內核之間的 “橋梁”,讓用戶能通過輸入命令,間接控制內核完成各種任務(如創建文件、啟動程序、管理進程等)。
二、?.sh
?腳本調用?.py
?腳本
一般用?.sh
?腳本調用?.py
?腳本,讓 Shell 負責搭建執行框架、和系統交互(如文件操作、環境配置、命令調用),Python 負責核心邏輯處理。
為什么不全用.py?
因為 Shell 更擅長 “對接操作系統”,Python 雖然能通過?os
?模塊調用系統命令,但本質是 “間接調用”。所以.sh搭配.py效率更高、分工更明確。
假設我們需要實現一個 “數據處理流水線”,功能包括:
- 檢查輸入數據目錄是否存在(Shell 負責);
- 創建輸出結果目錄(Shell 負責);
- 調用 Python 腳本處理數據(清洗、計算)(Python 負責);
- 壓縮處理后的結果(Shell 負責);
- 發送郵件通知任務完成(Shell 調用系統工具負責)。
Python 核心邏輯腳本(data_processor.py
)
通過命令行參數接收輸入 / 輸出路徑,負責具體的數據處理(如過濾異常值、計算平均值)。
# data_processor.py
import sys
import pandas as pddef main():# 從命令行參數獲取輸入文件路徑和輸出文件路徑if len(sys.argv) != 3:print("用法:python3 data_processor.py <輸入文件> <輸出文件>")sys.exit(1) # 異常退出,方便 Shell 捕獲錯誤input_path = sys.argv[1]output_path = sys.argv[2]try:# 核心邏輯:讀取數據、處理、保存結果df = pd.read_csv(input_path) # 用 pandas 處理 CSV 數據df_clean = df[df["value"] > 0] # 過濾異常值(假設 value 列不能為負)df_clean["mean"] = df_clean["value"].mean() # 計算平均值df_clean.to_csv(output_path, index=False)print(f"處理完成!結果已保存到 {output_path}")sys.exit(0) # 正常退出except Exception as e:print(f"處理失敗:{str(e)}", file=sys.stderr) # 錯誤信息輸出到 stderrsys.exit(1) # 異常退出if __name__ == "__main__":main()
Shell 腳本(pipeline.sh
)
負責流程控制、系統交互,調用 Python 腳本并處理前后的輔助任務。
#!/bin/bash
# pipeline.sh:數據處理流水線的控制腳本# 1. 定義參數(輸入/輸出路徑)
INPUT_DIR="./raw_data"
OUTPUT_DIR="./processed_data"
INPUT_FILE="${INPUT_DIR}/data.csv"
OUTPUT_FILE="${OUTPUT_DIR}/result.csv"
ZIP_FILE="${OUTPUT_DIR}/result.zip"
EMAIL="user@example.com" # 接收通知的郵箱# 2. 檢查輸入文件是否存在(Shell 系統交互能力)
if [ ! -f "${INPUT_FILE}" ]; thenecho "錯誤:輸入文件 ${INPUT_FILE} 不存在!" >&2 # 錯誤信息輸出到 stderrexit 1 # 退出腳本,終止流程
fi# 3. 創建輸出目錄(若不存在)
mkdir -p "${OUTPUT_DIR}" # -p 確保父目錄不存在時也能創建
echo "輸出目錄已準備:${OUTPUT_DIR}"# 4. 調用 Python 腳本處理數據(核心邏輯交給 Python)
echo "開始處理數據..."
python3 data_processor.py "${INPUT_FILE}" "${OUTPUT_FILE}"# 檢查 Python 腳本是否執行成功(通過退出碼判斷)
if [ $? -ne 0 ]; then # $? 是上一條命令的退出碼(0 為成功,非 0 為失敗)echo "數據處理失敗,終止流程" >&2exit 1
fi# 5. 壓縮處理結果(Shell 調用系統壓縮命令)
zip -q "${ZIP_FILE}" "${OUTPUT_FILE}" # -q 靜默模式,不輸出冗余信息
echo "結果已壓縮:${ZIP_FILE}"# 6. 發送郵件通知(Shell 調用系統郵件工具)
echo "數據處理流水線已完成,結果見附件" | mail -s "任務完成通知" -a "${ZIP_FILE}" "${EMAIL}"
echo "通知郵件已發送到 ${EMAIL}"# 7. 流程結束
echo "所有任務完成!"
exit 0
關鍵協作方式:
(1)參數傳遞
Shell 腳本通過?命令行參數?將必要信息(如文件路徑、配置項)傳遞給 Python 腳本,Python 用?sys.argv
?接收。
比如:.sh里面的一行
python3 data_processor.py input.csv output.csv
Python 中?sys.argv[1]
?接收?input.csv
,sys.argv[2]
?接收?output.csv
。
sys.argv[0]
?→?data_processor.py
(python腳本自身的文件名)
(2)環境變量共享
Shell 中定義的環境變量(如?export DATA_PATH="./data"
),可在 Python 中通過?os.environ
?讀取:
import os
data_path = os.environ.get("DATA_PATH")
(3)狀態反饋
Python 腳本通過?退出碼(sys.exit(0)
?表示成功,sys.exit(1)
?表示失敗)告訴 Shell 執行結果。Shell 用?$?
?變量獲取退出碼,判斷是否繼續后續流程(如示例中 “若 Python 失敗則終止腳本”)。
三、常見命令
1. echo 打印字符串或者變量值
注意:定義變量時,name="Amy"中間不能有空格
打印變量時,前面加上$標記:$name
和字符串混合打印時,變量名用{}包裹(解決邊界問題):${name}
| 打印當前用戶的主目錄(家目錄)路徑 |
| 打印$PATH, 存儲著系統查找可執行程序的目錄列表 |
| 存儲著當前用戶默認使用的 Shell 程序路徑?。 |
| 當前執行的腳本名稱 ($1 $2 $3 傳遞給這個腳本的第一二三個參數) |
2. cat 連接文件并打印其內容
查看單個文件內容:cat test.txt
查看多個文件內容:cat file1.txt file2.txt
創建新文件:cat > new_file.txt
輸入cat > new_file.txt
后回車,然后在終端中輸入想要寫入文件的內容,每輸入一行后按回車鍵換行,輸入完成后,按下Ctrl + D
組合鍵,即可將輸入的內容保存到new_file.txt
文件中。
復制文件內容到另一個文件:cat file1.txt file2.txt > combined.txt
這會把file1.txt
和file2.txt
的內容合并到combined.txt
文件中。如果combined.txt
不存在,會創建該文件,如果已存在,則會覆蓋原有內容。
追加內容到文件:cat >> file.txt
手動方式:
cat >> test.txt
這是追加的第一行內容
這是追加的第二行內容
(按 Ctrl + D 結束輸入)
自動方式(通過管道):
# 把 echo 輸出的內容追加到 file.txt
echo "這是通過 echo 追加的內容" | cat >> file.txt# 把 ls 命令的結果(當前目錄文件列表)追加到 file.txt
ls | cat >> file.txt# 把另一個文件的內容追加到 file.txt
cat other_file.txt | cat >> file.txt
3. ls
ls -a 顯示所有文件(包括以.開頭的隱藏文件)
存儲環境變量:.profile(用戶登錄時執行,只執行一次) 和 .bashrc (每次打開一個終端新建一個shell會話時執行)
一般推薦把環境變量放在.bashrc里
四、.sh腳本
1. 簡單例子
#!/bin/bash:告訴操作系統“這個腳本需要用哪個解釋器來執行”,這里指定要用 位于/bin
?目錄下的 Bash 解釋器
注意:要在 linux 環境執行,這里可以用?Git Bash?
chmod a+x tets.sh:chmod
?是 “change mode” 的縮寫,專門用于修改文件 / 目錄的訪問權限
./test.sh:執行當前目錄下?test.sh
?腳本
2. 進階例子
#!/bin/bash# 判斷一個數是否為素數
is_prime(){local num=$1# 素數判斷條件1:小于2的數(1、0、負數)一定不是素數if [ $num -lt 2 ]; thenreturn 1fi# 素數判斷條件2:遍歷從2到 sqrt(num) 的所有整數,檢查是否能整除 numfor ((i=2;i*i<=num;i++)); do# 若余數為0,說明 num 有除1和自身外的因數,不是素數if [ $((num%i)) -eq 0 ]; thenreturn 1fidonereturn 0
}read -p "請輸入一個正整數:" numberif is_prime $number; thenecho "$number 是素數"
elseecho "$number 不是素數"
fi
在函數中定義局部變量:加上local,比如 local num=1
$1:函數的第一個參數
$變量名
:取變量的值(如?$number
);
$(命令)
:取命令的輸出結果(如
current_dir=$(pwd) # 執行 pwd 命令(顯示當前目錄),將結果存入 current_dir
);
$((表達式))
:執行算術運算并返回結果(如?$((num%i))
?求余數,注意$((...))
?內部已經能自動解析變量,不需要再給變量加?$
)。
-lt:小于,less than
-gt:大于,greater than
-eq:等于,equal to? ?-ne:不等于,not equal to
-le:小于等于,less than or equal to
-ge:大于等于
(以?-
?開頭的字符串通常表示 “選項” 或 “操作符”)
?注意:0
真(成功)或?1
假(失敗),是人為規定的歷史慣例
對于一個命令 / 函數,“成功執行” 的結果只有一種(比如?ls
?成功列出文件),用?0
?表示足夠;
但 “失敗” 的原因有很多(比如文件不存在、權限不足、參數錯誤),可以用不同的非 0 值區分(例如?1
?表示通用錯誤,2
?表示權限錯誤,127
?表示命令不存在)。
if [ 條件判斷 ]; then# 條件為真時執行的代碼
else# 條件為假時執行的代碼
fi # 這里的 fi 表示整個 if 語句結束
注意:if [空格 ... 空格]; then
else
fi(fi
?是?if
?條件判斷語句的閉合標記,用于表示一個?if
?塊的結束,相當于?if
?的 “右括號”)
- 當調用?
is_prime $number
?時,函數本身已經輸出了 “真假” 對應的返回值; if
?可以直接接收這個返回值,無需再用?[]
?二次判斷。
for 變量名 in 列表; do# 循環體:對列表中的每個元素執行的命令
done# 例如
# 遍歷列表中的水果名稱
for fruit in 蘋果 香蕉 橙子 葡萄; doecho "我喜歡吃 $fruit"
done
或者:
for (( 初始值; 循環條件; 步長 )); do# 循環體:對每個數值執行的命令
done# 例如
# 輸出 1 到 5 的數字
for ((i=1; i<=5; i++)); doecho "當前數字:$i"
done
for ((...)); do
done
函數名() {# 函數體(一系列命令)
}函數名 參數1 參數2 ...
不需要?is_prime(參數)
?的形式
read -p "請輸入一個正整數:" number:
read [選項] 變量名
read?
讀取用戶輸入,并將輸入內容賦值給后面指定的變量 number
選項 -p "提示信息"?輸入前顯示提示信息
3. 猜數字游戲
#!/bin/bash# 把生成的隨機數賦值給number
number=$(shuf -i 1-10 -n 1)
echo $numberwhile true; doread -p "請輸入一個1-10之間的數字:" guessif [ $guess -eq $number ]; thenecho "恭喜你,猜對了!是否繼續?(y/n):"read choiceif [ $choice == 'y' ] || [ $choice == 'Y' ]; thennumber=$(shuf -i 1-10 -n 1)echo $numbercontinueelsebreakfi
elif [ $guess -gt $number ]; thenecho "猜大了"
elseecho "猜小了"
fidone
shuf -i 1-10 -n 1:從 1 到 10 之間隨機選出 1 個整數
# 把生成的隨機數賦值給number
number=$(shuf -i 1-10 -n 1)
echo $number
[[]] 比 [] 更加兼容、更強大、不易出錯(但是現在好像[]很多都可以):
特性 | [] (test 命令) | [[ ]] (Bash 擴展測試) |
---|---|---|
本質 | 是?/bin/test ?命令的簡寫(外部命令) | Bash 內置的擴展語法(更強大的內部實現) |
字符串比較 | 需用?= ,且變量必須加引號(如?[ "$a" = "$b" ] ) | 支持?= ?或?== ,變量可省略引號(如?[[ $a == $b ]] ) |
正則匹配 | 不支持 | 支持?=~ ?進行正則匹配(如?[[ $str =~ ^[0-9]+$ ]] ) |
邏輯運算符 | 用?-a (與)、-o (或),且優先級低 | 用?&& 、|| |
數值比較 | 支持?-eq 、-gt ?等(與?[[ ]] ?相同) | 支持?-eq 、-gt ?等,也支持?< 、> (需注意空格) |
變量空值處理 | 變量為空時易報錯(需加引號避免) | 變量為空時自動處理,更安全 |
while [[ $guess -ne $number ]],雖然在while之前guess還沒獲取,但是[[]]會自動給guess賦值為0