戰(zhàn):從命令行到自動(dòng)化運(yùn)維的完整套路)
寫Shell腳本這事兒很多人都是被逼出來的。我自己就是先被一串又一串的命令搞得頭大然后發(fā)現(xiàn)“命令行是一條一條喂給電腦的腳本是一次性把規(guī)則告訴電腦”從那以后只要是需要反復(fù)執(zhí)行的活兒我第一反應(yīng)就是“能不能寫個(gè)腳本”。Shell腳本說白了就是把一串Linux命令按邏輯組合起來放進(jìn)一個(gè)文件里賦予執(zhí)行權(quán)限后一次跑完。它能干的事非常多批量備份、日志清理、監(jiān)控告警、自動(dòng)部署、數(shù)據(jù)處理……凡是我們?cè)诮K端里手動(dòng)敲過兩遍以上的命令都值得腳本化。這篇文章適合剛接觸Linux沒太久、想從“會(huì)敲命令”邁向“會(huì)寫腳本”的人也適合那些已經(jīng)寫了幾個(gè)腳本但總覺得自己在“硬湊”的人我會(huì)把日常積累的套路、坑、調(diào)試方法一起倒出來。1. 我為什么勸你認(rèn)真學(xué)Shell腳本而不是只靠記憶命令行1.1 從“會(huì)敲命令”到“會(huì)寫腳本”的分水嶺剛接觸Linux的時(shí)候我特別崇拜那些能一口氣敲出一串復(fù)雜命令的人感覺那才是高手。后來發(fā)現(xiàn)真正的高手從來不靠手速他們靠的是把復(fù)雜邏輯固化下來。Shell腳本就是這個(gè)“固化”的手段??梢赃@么理解命令行是“手動(dòng)擋”腳本是“自動(dòng)駕駛”。手動(dòng)擋當(dāng)然靈活每個(gè)路口你都能自己控制但如果是每天上班通勤的固定路線沒有人會(huì)愿意天天手動(dòng)換擋。腳本存在的意義就是把那些重復(fù)的、固定的、容易忘記操作步驟的任務(wù)交給一個(gè)可復(fù)現(xiàn)的文件去執(zhí)行。從“會(huì)敲命令”到“會(huì)寫腳本”你還需要跨過幾個(gè)小臺(tái)階理解腳本就是一個(gè)“命令的集合”但集合里還有變量、循環(huán)、判斷。搞懂執(zhí)行機(jī)制腳本里的每一行本質(zhì)上還是在啟動(dòng)一個(gè)子Shell去執(zhí)行命令。學(xué)會(huì)給腳本傳參數(shù)和接收輸出。學(xué)會(huì)處理異常命令失敗了是繼續(xù)跑還是停下來。很多人卡在第二個(gè)臺(tái)階上因?yàn)榭傁胫涯_本寫出“程序”的樣子一上來就定義函數(shù)搞面向?qū)ο竽翘捉Y(jié)果把自己繞暈了。我的經(jīng)驗(yàn)是先用最簡(jiǎn)單的順序結(jié)構(gòu)解決眼下問題等腳本寫多了再回頭優(yōu)化結(jié)構(gòu)。1.2 從需求到腳本一個(gè)最樸素的分步思路我在寫腳本前后心態(tài)上的轉(zhuǎn)變其實(shí)就一句話“先想清楚要干什么再想怎么敲命令?!庇幸淮挝倚枰謇砟硞€(gè)目錄下七天前的日志文件手動(dòng)敲的流程是先看目錄里有哪些文件再篩選七天前的然后刪除最后確認(rèn)一下磁盤空間有沒有釋放。第二周我又要做同樣的事第三周還是同樣的事我就知道該寫腳本了。這個(gè)從“手動(dòng)操作”到“寫腳本”的轉(zhuǎn)化過程其實(shí)可以總結(jié)成四步把步驟寫成大白話找到目錄、篩選舊文件、刪除、報(bào)告結(jié)果。把大白話翻譯成命令find、rm或者再加上df。把命令按照運(yùn)行順序?qū)戇M(jìn)文件。加參數(shù)支持、加判斷邏輯、加輸出信息讓腳本更健壯。別看這四步簡(jiǎn)單絕大多數(shù)腳本卡住不是因?yàn)椴粫?huì)寫命令而是卡在第二步和第三步的銜接上。你以為一條命令能搞定實(shí)際上命令在交互式終端里能用放到腳本里就各種問題——比如變量沒加引號(hào)、忘記處理空格路徑、rm刪錯(cuò)了目錄。這些問題我下面會(huì)專門講到。2. 寫腳本前必須養(yǎng)成的幾個(gè)習(xí)慣2.1 Shabang行和權(quán)限兩個(gè)最常見的入門坑第一個(gè)坑是腳本第一行的#!也就是Shebang行。很多文章會(huì)說“必須寫”但沒解釋為什么。其實(shí)原理很直接當(dāng)你直接執(zhí)行./myscript.sh時(shí)操作系統(tǒng)需要知道用哪個(gè)解釋器來解析這個(gè)文件里的內(nèi)容。#!/bin/bash就是明確告訴系統(tǒng)“用/bin/bash去跑這個(gè)文件”。如果漏了這行系統(tǒng)會(huì)默認(rèn)用當(dāng)前Shell去解釋不同環(huán)境下的行為可能完全不一樣非常容易踩坑。操作層面建議直接寫#!/bin/bash不要用#!/bin/sh。因?yàn)椴簧侔l(fā)行版里/bin/sh已經(jīng)指向dash或者別的解釋器語法兼容性會(huì)差一些我在某臺(tái)服務(wù)器上就吃過這個(gè)虧本地跑得好好的腳本一放到服務(wù)器上就各種語法報(bào)錯(cuò)查來查去發(fā)現(xiàn)是解釋器不一致導(dǎo)致的。第二個(gè)坑是執(zhí)行權(quán)限。很多人寫完腳本一執(zhí)行報(bào)Permission denied第一反應(yīng)是“腳本寫錯(cuò)了”其實(shí)只是少了個(gè)執(zhí)行權(quán)限。一開始我習(xí)慣用chmod x script.sh去處理后來發(fā)現(xiàn)更穩(wěn)妥的方式是chmod 755 script.sh直接明確所有者有讀寫執(zhí)行權(quán)限其他用戶只有讀和執(zhí)行權(quán)限。這部署到多用戶環(huán)境時(shí)能省掉很多不必要的權(quán)限問題。2.2 變量、引號(hào)、通配符看著簡(jiǎn)單坑里都是過來人Shell腳本里的變量定義nameabc注意等號(hào)兩邊不能有空格。這個(gè)我想很多初學(xué)的人都遇到過習(xí)慣性寫成name abcShell會(huì)把name當(dāng)成一個(gè)命令去執(zhí)行然后報(bào)command not found。這不是你不夠細(xì)心而是Shell語法就是這么“不順手”不踩一下根本記不住。引號(hào)是另一個(gè)重災(zāi)區(qū)。雙引號(hào)里的變量會(huì)展開單引號(hào)里的內(nèi)容就是純文本。比如nameworld echo hello $name echo hello $name第一次輸出hello world第二次輸出hello $name。這個(gè)問題看起來簡(jiǎn)單但一旦腳本里要拼接路徑、拼接命令參數(shù)時(shí)有沒有加引號(hào)導(dǎo)致的差別非常大。特別是文件路徑里帶空格的時(shí)候——如果變量不加雙引號(hào)Shell會(huì)把它拆成多個(gè)獨(dú)立的詞去處理命令直接就是錯(cuò)的。通配符*、?、[]也得好小心。用rm *.log的時(shí)候Shell會(huì)先把*.log匹配到的文件列表展開再交給rm執(zhí)行。但如果目錄下的文件特別多命令行的長(zhǎng)度有限制可能直接報(bào)錯(cuò)。更安全的方式是用find去配合處理后面我會(huì)給案例。2.3 命名規(guī)范與注釋給三個(gè)月后的自己留條活路寫腳本的時(shí)候加注釋這件事我真的吃了很多虧才養(yǎng)成習(xí)慣。倒不是有多難就是當(dāng)時(shí)總覺得“這是我自己的腳本我肯定記得住”。事實(shí)是過三個(gè)月再看你很可能完全不記得當(dāng)初為什么用了一個(gè)那么奇怪的寫法。我的建議是至少做到這幾點(diǎn)腳本文件命名用小寫字母加下劃線比如backup_logs.sh不要用a.sh這種沒有信息的名字。全局變量用大寫加下劃線比如LOG_DIR函數(shù)內(nèi)的局部變量用小寫。每個(gè)函數(shù)上方用兩三行注釋說明它干什么、接收什么參數(shù)。關(guān)鍵命令前加注釋解釋“為什么這么做”特別是那些看起來很繞的寫法。比如我之前寫過一段判斷磁盤使用率的邏輯當(dāng)時(shí)覺得很簡(jiǎn)單沒加注釋后來回看時(shí)一直在糾結(jié)“為什么這里要多一個(gè)倒數(shù)第二層的管道”。這種時(shí)候才意識(shí)到注釋是寫給未來的自己看的不是寫給現(xiàn)在的。3. 一個(gè)完整的實(shí)操案例日志歸檔腳本3.1 需求梳理先別急著寫代碼前面講了那么多理論這里我拿一個(gè)非常典型的場(chǎng)景來說日志歸檔。假設(shè)有這樣一個(gè)需求——某應(yīng)用每天會(huì)往/var/log/myapp/目錄下寫入日志文件日志按天命名例如app-2025-06-01.log?,F(xiàn)在需要寫一個(gè)腳本把七天前的日志打包壓縮然后刪除原始文件釋放磁盤空間整個(gè)過程要有日志輸出方便事后追蹤。這個(gè)需求看起來很常規(guī)但動(dòng)手之前還是先梳理清楚幾個(gè)問題目錄路徑是否固定如果腳本被別人拿去用路徑最好不要硬編碼?!捌咛烨啊痹趺唇缍ò次募锏娜掌谶€是按文件的mtime修改時(shí)間我建議按文件名來因?yàn)閙time可能因?yàn)閺?fù)制、修改而變化。要不要保留最近七天的文件要。刪除原始文件前是不是必須先確認(rèn)壓縮成功必須。腳本如果出錯(cuò)了怎么讓人知道要有錯(cuò)誤信息。想清楚這幾點(diǎn)再去寫腳本順序邏輯就非常清晰了。3.2 分步實(shí)現(xiàn)每一段代碼都說明白為什么這么寫下面是我后來實(shí)際用過的腳本簡(jiǎn)化版我分段拆開來解釋。#!/bin/bash # # 日志歸檔腳本將七天前的日志壓縮并刪除原文件 # 用法./archive_logs.sh 日志目錄 LOG_DIR${1:-/var/log/myapp} RETENTION_DAYS7 TIMESTAMP$(date %Y%m%d_%H%M%S) ARCHIVE_DIR${LOG_DIR}/archive LOG_FILE${ARCHIVE_DIR}/archive_${TIMESTAMP}.log這里用${1:-/var/log/myapp}處理參數(shù)如果用戶傳了目錄就用用戶傳的如果沒傳就用默認(rèn)值。這個(gè)寫法是我個(gè)人強(qiáng)烈推薦的能讓腳本更通用不用為每一個(gè)目錄單獨(dú)復(fù)制一份腳本。接下來創(chuàng)建歸檔目錄和日志文件目錄mkdir -p $ARCHIVE_DIR-p的意思是不只創(chuàng)建目錄如果上級(jí)目錄不存在也會(huì)一并創(chuàng)建而且如果目錄已經(jīng)存在不會(huì)報(bào)錯(cuò)。這是寫腳本時(shí)很常用的一個(gè)選項(xiàng)。然后是核心的查找和壓縮邏輯cd $LOG_DIR || { echo 無法進(jìn)入目錄 $LOG_DIR; exit 1; } find . -maxdepth 1 -type f -name app-*.log -mtime $RETENTION_DAYS -print0 | while IFS read -r -d file; do base$(basename $file) tar -czf ${ARCHIVE_DIR}/${base}.tar.gz $base if [ $? -eq 0 ]; then rm -f $base echo [OK] 歸檔并刪除: $base else echo [FAIL] 歸檔失敗已保留原文件: $base 2 fi done這里有幾個(gè)關(guān)鍵點(diǎn)我要專門講講。第一為什么用find -mtime 7而不是用日期字符串mtime是最近修改時(shí)間7表示超過七天了。雖然我前面說過用文件名更準(zhǔn)但實(shí)際情況下如果日志文件不是嚴(yán)格按天命名mtime也是能接受的選擇。更嚴(yán)格的做法你可以用! -newermt 7 days ago來處理但這個(gè)寫法不同find版本支持情況不同你本地測(cè)試通過了就好。第二為什么用-print0while read而不是直接find ... -exec因?yàn)槿罩疚募m然是我們自己生成的但保不準(zhǔn)會(huì)帶空格或者其他特殊字符。-print0會(huì)讓find輸出文件名時(shí)用空字符而不是換行來分隔配合read -d 就能安全地處理任意文件名。這個(gè)習(xí)慣我強(qiáng)烈建議養(yǎng)成能讓你避開很多隱藏很深的坑。第三刪除前一定要確認(rèn)壓縮成功。我用$?檢查上一條命令的返回值tar成功了是0失敗是非0只有成功了才執(zhí)行rm。這個(gè)習(xí)慣特別重要——寧可壓縮失敗留著舊文件也不要?jiǎng)h了原文件后才發(fā)現(xiàn)壓縮包是壞的。3.3 加上容錯(cuò)和日常運(yùn)維需要的輸出實(shí)戰(zhàn)中還有一個(gè)容易忽略的問題就是腳本執(zhí)行過程中如果中途出錯(cuò)后面的命令還會(huì)不會(huì)繼續(xù)執(zhí)行默認(rèn)情況下Shell腳本不會(huì)因?yàn)槟硹l命令失敗就停下來。這就可能導(dǎo)致壓縮失敗了但刪除還是執(zhí)行了。一個(gè)比較穩(wěn)妥的思路是在腳本開頭加set -euo pipefailset -e一旦腳本中任何命令返回非零狀態(tài)立即退出腳本。set -u如果使用了未定義的變量直接報(bào)錯(cuò)退出。set -o pipefail管道命令中如果任何一段失敗整個(gè)管道的返回值就是失敗的。這個(gè)組合我是在踩過幾次坑之后才真正接受并養(yǎng)成的習(xí)慣。剛開始覺得set -e太“激進(jìn)”了可能會(huì)導(dǎo)致腳本在遇到一些“預(yù)期內(nèi)的小失敗”時(shí)直接退出。后來我調(diào)整了寫法明知某條命令可能會(huì)失敗、但希望腳本繼續(xù)往下走的命令就在末尾加|| true。這樣既能有安全網(wǎng)又能靈活處理預(yù)期中的失敗。日志輸出方面我的心得是“不要只把消息寫給終端還要落盤”。腳本如果是通過定時(shí)任務(wù)跑的執(zhí)行完之后你根本看不到終端輸出所以要把關(guān)鍵信息寫到日志文件里方便事后排查。腳本里我設(shè)置了LOG_FILE但為了簡(jiǎn)化就沒在每一行加重定向?qū)嶋H你可以直接把需要落盤的輸出用 ${LOG_FILE} 21追加進(jìn)去。4. 測(cè)試和調(diào)試別讓腳本裸奔上線4.1 用bash -x觀察腳本的每一步“內(nèi)心戲”最開始我寫腳本寫完直接執(zhí)行報(bào)錯(cuò)就盯著錯(cuò)誤信息猜。后來朋友給我推薦了bash -x直接讓我打開了新世界bash -x ./archive_logs.sh執(zhí)行的時(shí)候Shell會(huì)把每一行命令展開后的實(shí)際內(nèi)容打印到終端比如變量值替換成什么、命令拼接成什么樣都能看得清清楚楚。這功能解釋“腳本怎么回事”的時(shí)候太有用了。比如之前我寫了一個(gè)循環(huán)拼接字符串的腳本結(jié)果發(fā)現(xiàn)輸出的路徑老是少了一段怎么查都查不出來。后來用bash -x一跑清清楚楚看到變量在循環(huán)里被覆蓋掉了問題一下定位?,F(xiàn)在我的排查習(xí)慣是腳本執(zhí)行前先bash -n檢查語法再bash -x看執(zhí)行過程配合起來非常好用。bash -n是只檢查語法不執(zhí)行適合在正式執(zhí)行前先用它過一遍防止低級(jí)錯(cuò)誤。不過它只能查語法查不出運(yùn)行時(shí)才會(huì)暴露的問題所以調(diào)試階段的bash -x不能省。4.2 常見報(bào)錯(cuò)信息速查看到這些別再慌平時(shí)跑腳本的時(shí)候我積累了幾類高頻報(bào)錯(cuò)這里整理成表格方便大家直接對(duì)照?qǐng)?bào)錯(cuò)信息常見原因排查方向command not found命令拼錯(cuò)了或者環(huán)境變量PATH沒包含命令路徑檢查命令名必要時(shí)用絕對(duì)路徑Permission denied腳本沒有執(zhí)行權(quán)限chmod x script.shNo such file or directory打開文件時(shí)路徑不對(duì)或者Shabang行指定的解釋器路徑錯(cuò)誤檢查#!/bin/bash是否拼對(duì)檢查文件路徑unexpected end of file經(jīng)常是少了fi、done、}檢查循環(huán)和判斷語句的結(jié)尾是否配對(duì)integer expression expected比較時(shí)用了字符串而不是數(shù)字檢查變量里存的是不是數(shù)字unbound variable用了set -u后變量沒定義給變量加默認(rèn)值或提前定義遇到報(bào)錯(cuò)我的處理順序是先看是哪一行再確定是語法層面還是運(yùn)行層面然后用bash -x跑一遍看實(shí)際的執(zhí)行過程最后再搜索報(bào)錯(cuò)的完整消息。而不是一上來就復(fù)制報(bào)錯(cuò)去搜。4.3 自動(dòng)化測(cè)試的思路讓舊腳本不再“哪天崩了都不知道”另外一個(gè)容易被忽略的問題是腳本改了一次之后怎么確定沒改壞尤其是那些放在定時(shí)任務(wù)里運(yùn)行的腳本有時(shí)候一個(gè)月才出一次問題等發(fā)現(xiàn)問題的時(shí)候日志已經(jīng)堆了很久了。我自己用的方法是給腳本做一個(gè)簡(jiǎn)易的“冒煙測(cè)試”用一個(gè)臨時(shí)目錄模擬真實(shí)環(huán)境放上測(cè)試文件。把腳本跑一遍檢查是否有錯(cuò)誤輸出。檢查歸檔包是否生成、原文件是否被清理。這聽起來很樸素但能擋住大部分低級(jí)回歸。有條件的話還可以給腳本加一個(gè)--dry-run選項(xiàng)只打印即將執(zhí)行的命令而不真正執(zhí)行這對(duì)自己排查問題和給別人演示腳本都特別友好。5. 日常高頻場(chǎng)景腳本套路可以直接抄5.1 批量重命名和批量壓縮一個(gè)套路走天下日常用得最多的兩大類腳本就是批量重命名和批量壓縮。我這里分享兩個(gè)模板思路。批量重命名時(shí)核心是“循環(huán)每個(gè)文件”和“生成新文件名”#!/bin/bash # 批量將當(dāng)前目錄下的 .txt 文件重命名為 .md for file in *.txt; do base${file%.txt} mv $file ${base}.md done這里${file%.txt}是Shell的參數(shù)展開意思是把變量file末尾的.txt去掉。這種寫法比用sed再去提取文件名要直觀得多。批量壓縮時(shí)我遇到過一個(gè)問題直接把所有需要壓縮的文件打包成一個(gè)包和每個(gè)文件單獨(dú)打包需求不一樣。常規(guī)做法是# 將所有 .log 文件打包成一個(gè)包 tar -czf all_logs.tar.gz *.log # 分別打包每個(gè)日期目錄 for dir in 2025-*; do tar -czf ${dir}.tar.gz $dir done需要提醒的是如果目錄下有幾百上千個(gè)文件*.log展開可能會(huì)超過命令行長(zhǎng)度限制。這種場(chǎng)景我一般會(huì)改用find配合-print0的方式再結(jié)合前面提到的while read安全性和穩(wěn)定性都會(huì)高很多。5.2 定時(shí)任務(wù)與腳本的配合別踩PATH和環(huán)境的坑腳本寫好之后最常見的部署方式就是放進(jìn)cron定時(shí)任務(wù)里。但這里有個(gè)特別隱蔽的坑cron執(zhí)行腳本時(shí)環(huán)境變量和交互式終端里完全不一樣PATH可能非常精簡(jiǎn)不包含/usr/local/bin這類路徑。我在這里真實(shí)踩過坑某次腳本里用了一個(gè)工具交互式終端里跑得好好的但放進(jìn)cron之后一直報(bào)command not found原因就是cron的環(huán)境里沒有那個(gè)工具所在的目錄。后來我在腳本開頭加了一行顯式設(shè)置PATHexport PATH/usr/local/bin:/usr/bin:/bin:${PATH}從那以后我的所有腳本只要可能有部署到定時(shí)任務(wù)需求的都會(huì)在開頭處理一下PATH這是一個(gè)簡(jiǎn)單的防御性寫法。另外cron里運(yùn)行的腳本如果有輸出默認(rèn)會(huì)通過郵件發(fā)給本機(jī)用戶但很多服務(wù)器根本沒配郵件服務(wù)導(dǎo)致錯(cuò)誤信息被丟棄。我建議在cron配置里把標(biāo)準(zhǔn)輸出和標(biāo)準(zhǔn)錯(cuò)誤重定向到日志文件例如30 2 * * * /home/user/scripts/archive_logs.sh /home/user/logs/archive.log 21這樣即使腳本出了問題至少日志里能看到痕跡。5.3 監(jiān)控腳本的寫法取指標(biāo)、做判斷、發(fā)通知很多人以為監(jiān)控腳本很復(fù)雜其實(shí)核心就是三件事獲取指標(biāo)、判斷閾值、通知。以磁盤使用率為例#!/bin/bash # 磁盤使用率監(jiān)控超過閾值則輸出告警 THRESHOLD80 CURRENT$(df -h / | awk NR2 {print $5} | sed s/%//) if [ $CURRENT -gt $THRESHOLD ]; then echo 磁盤使用率已超過閾值: ${CURRENT}% # 這里可以接入通知比如寫入告警文件、調(diào)用接口等 else echo 磁盤使用率正常: ${CURRENT}% fi這個(gè)腳本里有個(gè)小細(xì)節(jié)df -h | awk NR2 {print $5}取出的是根分區(qū)的使用率百分比帶一個(gè)百分號(hào)再用sed s/%//去掉百分號(hào)才能和THRESHOLD做數(shù)值比較。這里繞了一下原因就是Shell里的數(shù)字比較不支持百分號(hào)兩者之間不是一個(gè)數(shù)字類型。監(jiān)控腳本和前面幾個(gè)腳本不同它通常要跑很多年所以我覺得最值得花時(shí)間的地方不是一上來就寫得很復(fù)雜而是把“收集指標(biāo)”和“發(fā)出告警”這兩件事拆成兩個(gè)函數(shù)將來要增加告警渠道時(shí)只需要改一個(gè)函數(shù)就夠了。6. 我在實(shí)際使用中的一些體會(huì)說了這么多最后聊一點(diǎn)個(gè)人體會(huì)。寫腳本這件事說到底看的不是你能寫出多高深的語法而是你能不能在寫之前把流程理清楚。我見過不少人一上來就寫結(jié)果腳本里全是“補(bǔ)丁式”的邏輯這里加一句特殊情況判斷那里加一個(gè)臨時(shí)變量最后腳本又長(zhǎng)又難讀。其實(shí)大多數(shù)腳本要解決的場(chǎng)景都是重復(fù)勞動(dòng)只要把流程拆清楚腳本自然就好寫了。我自己的習(xí)慣是先手工跑一遍命令確認(rèn)每一步都能得到預(yù)期結(jié)果再改寫成腳本。這個(gè)過程能篩掉很多邏輯問題比寫完一堆再調(diào)試要省太多時(shí)間。另外腳本里能寫得通用一點(diǎn)就通用一點(diǎn)哪怕只是把“固定路徑”改成“可通過參數(shù)傳入”都會(huì)讓腳本的壽命長(zhǎng)很多。再分享一個(gè)最后的技巧腳本最好在一開始就做“冪等性”設(shè)計(jì)也就是同一個(gè)腳本多次運(yùn)行不會(huì)因?yàn)橹貜?fù)執(zhí)行而產(chǎn)生錯(cuò)誤結(jié)果。比如歸檔腳本跑第一次會(huì)把七天前的日志歸檔掉跑第二次再運(yùn)行就不會(huì)重復(fù)歸檔同一天的日志因?yàn)橐呀?jīng)刪掉了。這個(gè)思路能讓腳本在cron里偶爾被誤觸多次時(shí)也安全。Shell腳本的價(jià)值不在技術(shù)多高深而在“解放雙手”這四個(gè)字。把第一次做覺得麻煩的事記錄下來變成每次都能自動(dòng)完成的事這個(gè)積累過程才是它最有魅力的地方。