# Shell Script ## Shell Script的種類及介紹 1. sh(Bourne Shell): Bourne Shell 是由 AT&T 公司的 Steve Bourne在1977年開發,為了紀念Bourne,以他的名字命名。sh是第一個流行的shell,1979 年第一個流行的 Unix 版本7發行時, 開始使用Bourne shell,雖然 Unix 上的 shell 有許多種, 但許多 Unix 系統至今仍然使用 sh 做為重要的管理工具。 2. bash(Bourne-Again Shell): bash 是sh的增強版本,在1987年由布萊恩·福克斯為了GNU計劃而編寫。它的名稱來自Bourne shell(sh)的一個雙關語(Bourne again / born again):Bourne-Again SHell。 1989年釋出第一個正式版本,原先是計劃用在GNU作業系統上,但能執行於大多數類Unix系統的作業系統之上,包括Linux與Mac OS X v10.4起至macOS Mojave都將它作為預設shell,而自macOS Catalina,預設Shell以zsh取代。 bash保持了對 sh的兼容性,而且是完全免費的,屬於開源軟體(Open Source)。它提供了更多功能、更強大的命令解釋、命令歷史記錄和許多實用的功能。 Linux 作為預設的 Shell 是因為它具有以下特色: * 可以使用類似DOS下面的doskey的功能,用上下方向鍵查閱和快速輸入並修改命令。 * 自動通過查找的方式,給出以某字串開頭的命令。 * 包含了自身的幫助功能,你只要在提示符下面鍵入help就可以得到相關的幫助訊息。 5. csh(C Shell): csh 提供了類似於 C 程式語言的語法,它引入了一些新的功能,如命令縮寫和歷史記錄展開。 6. zsh(Z Shell): 保羅·弗斯塔德(Paul Falstad)於1990年在普林斯頓大學求學時編寫了Zsh的初版。名稱源於耶魯大學教授邵中(Zhong Shao,後轉為普林斯頓大學教授), 保羅將教授的使用者名稱"zsh"作為此Shell的名稱。 zsh 是 bash 的替代選擇,它具有更強大的自動完成、更好的命令歷史記錄和自定義配置選項。它支援腳本編程和交互式使用。自2019年起,macOS的預設Shell已從Bash改為Zsh。 8. tcsh(Tenex C Shell): tcsh 是 csh 的改進版本,它增加了更多的命令縮寫、命令歷史記錄和命令行編輯功能。 10. fish(Friendly Interactive Shell): fish 是一個用戶友好的 shell,提供了更好的自動完成、語法高亮和易於使用的界面。 ### sh, bash, zsh的差異 sh: * 功能相對有限,不具備像後來的 shell 那麼多的功能。 * 在許多Unix系統上,sh實際上是指向其他更現代化的shell,例如 bash。 * 主要用於腳本編程,而不太適合交互式使用。 zsh(Z Shell): * 具有強大的自動完成功能,能夠根據上下文預測和提供可能的命令選項。 * 支援更高級的命令歷史記錄,包括更好的歷史搜索和展開功能。 * 提供自定義的配置選項,使用戶可以根據個人偏好自訂 shell 的行為和外觀。 * 擁有一些用戶友好的特性,如更好的提示、語法高亮和彩色輸出。 bash(Bourne-Again Shell): * 提供了許多在sh中缺少的功能,如命令歷史、自動完成、命令行編輯等。 * 具有豐富的內建命令和工具,使得腳本編程更加方便和強大。 * 廣泛使用於系統管理、腳本編程和交互式使用。 ## Shell Script 常用關鍵字 * **#!/bin/sh** 運行Shell腳本時所要使用的Shell解釋器 * **if、elif、else、fi: 條件判斷和控制語句。** ```bash= score=85 if [ $score -ge 90 ]; then echo "成績優秀" elif [ $score -ge 80 ]; then echo "成績良好" else echo "成績普通" fi ``` :::spoiler ```bash $ cat test #!/bin/bash if [ $aaa != "" ]; then echo "aaa not empty!!" fi ``` ::: * **case、esac: 多分支判斷語句。** ```bash= fruit="蘋果" case "$fruit" in "蘋果") echo "選擇了蘋果" ;; "橘子") echo "選擇了橘子" ;; *) echo "未知水果" ;; esac ``` * **for、in、do、done: 迴圈語句。** ```bash= for num in 1 2 3 4 5; do echo "數字:$num" done ``` * **while、do、done: 條件迴圈語句。** ```bash= count=1 while [ $count -le 5 ]; do echo "計數:$count" count=$((count + 1)) done ``` * **until、do、done: 在條件為假時迴圈語句。** ```bash= x=0 until [ $x -ge 5 ]; do echo "數值:$x" x=$((x + 1)) done ``` * **function、return: 定義和調用函數,函數可以返回值。** ```bash= my_function() { echo "這是一個函數" } my_function ``` :::spoiler **funtion傳參數簡要說明** **定義一個function** ``` show_params() { echo "第一個參數:$1" echo "第二個參數:$2" echo "第三個參數:$3" } ``` **調用function,並傳遞三個參數** ``` show_params "$1" "$2" "$3" ``` **命令執行** ``` ./myscript.sh 參數1 參數2 參數3 ``` **輸出** 第一個參數:參數1 第二個參數:參數2 第三個參數:參數3 **補充** 如果需要將函數的輸出作為參數傳遞給其他函數時才需要() ``` result=$(my_function "$param1" "$param2") ``` ::: * **local: 在函數內聲明局部變數,僅在函數範圍內有效。** ```bash= my_function() { local name="John" echo "姓名:$name" } my_function ``` * **read: 從標準輸入中讀取用戶的輸入。** ```bash= echo "請輸入姓名:" read name echo "你好,$name!" ``` * **echo: 輸出文字到標準輸出。** ```bash= echo "歡迎來到Shell脚本世界!" ``` * **shift: 移動參數位置,通常在函數內使用。** ```bash= my_function() { shift echo "第一個參數:$1" } my_function 1 2 3 ``` * **export: 將變數加入環境中,使其在子進程中可見。** ```bash= export MY_VARIABLE="值" ``` * **source(或 . ): 在當前Shell環境中執行腳本,將其中的變數和函數引入當前環境。** ```bash= source my_script.sh ``` * **readonly: 將變數設置為只讀,防止其被修改。** ```bash= readonly MY_VARIABLE="值" ``` * **declare(或 typeset): 聲明變數的屬性,如陣列、整數等。** ```bash= declare -a my_array=(1 2 3) ``` * **unset: 刪除變數或函數。** ```bash= unset MY_VARIABLE ``` * **break、continue: 控制迴圈的流程,break終止迴圈,continue跳過當前迭代。** ```bash= for num in 1 2 3 4 5; do if [ $num -eq 3 ]; then continue fi echo "數字:$num" if [ $num -eq 4 ]; then break fi done ``` ## Shell Script 比較運算符 * **-eq** : 兩數值相等 (equal) * **-ne** :兩數值不等 (not equal) * **-gt** :n1 大於 n2 (greater than) * **-lt** :n1 小於 n2 (less than) * **-ge** :n1 大於等於 n2 (greater than or equal) * **-le** :n1 小於等於 n2 (less than or equal) ## `sed` 流編輯器(Stream Editor)  - `-e`: 執行Script(預設選項) ```bash $ echo 'This is a book' | sed 's/is/IS/g' <- 將字串〝is〞改為〝IS〞(單字前沒加空隔) ThIS IS a book $ echo 'This is a book' | sed 's/is/IS/' ThIS is a book $ echo 'This is a book' | sed 's/[[:space:]]/@/g' This@is@a@book $ echo 'This is a book' | sed 's/ /@/g' This@is@a@book $ echo 'This is a book' | sed 's/^[A-Z]his/It/g' It is a book $ echo 'This is a book' | sed 's/^[A-Z]/It/g' Ithis is a book $ echo 'This is a book/paper' | sed 's/\/paper/ and a peper/g' This is a book and a peper ``` 修改檔案內容 ```bash $ cat temp.txt AAA BBB CCC $ sed 's/[B]/T/g' temp.txt AAA TTT CCC $ cat temp.txt AAA BBB CCC $ sed 's/[B]/T/g' temp.txt > temp.txt #the same as $ sed -i 's/[B]/T/g' temp.txt ``` ## `awk` AWK 是取自三位創始人 Alfred Aho,Peter Weinberger, 和 Brian Kernighan 的 Family Name 的首字符 grep 更適合單純的查找或匹配文本 sed 更適合編輯匹配到的文本 awk 更適合格式化文本,對文本進行較複雜格式處理 示範用awk提取特定文檔的內文 ``` data.txt Alice 25:Female Bob 30:Male Carol 22:Female ``` ``` test.awk #!/bin/awk -f # 設置字段分隔符為空格 BEGIN { FS = " " } { print $2 } ``` ```bash= # $2 表示提取每行第二个字段的内容,并使用 print 命令输出 $ awk '{print $2}' data.txt 25:Female 30:Male 22:Female # -F 指定用特定符號分隔,再取指定字段印出 $ awk -F':' '{print $2}' data.txt 25 30 22 # 也可以寫成以下 $ awk 'BEGIN{FS=":"} {print $2}' data.txt 25 30 22 # 設置變量,這樣會執行1 + a的字段結果 $ awk -va=1 '{print $1+a}' data.txt 25:Female 30:Male 22:Female # 輸出包含指定文字的行 $ awk '/Female/ ' data.txt Alice 25:Female Carol 22:Female # 加入邏輯運算,字串用 "" $ awk -F':' '$2>22 && $3=="Female" {print $1,$2,$3}' data.txt Alice 25 Female # 用-f執行awk檔 $ awk -f test.awk data.txt # 計算文件大小, |將前面的輸出作為後面的輸入使用,串聯ls -l跟 awk $ ls -l *.txt | awk '{sum+=$5} END {print sum}' ``` ## cat cat:輸出檔案內容至螢幕或檔案 -n 或 --number﹕由 1 開始對所有輸出的行數編號 -b 或 --number-nonblank﹕和 -n 相似,只不過對於空白行不編號 -s 或 --squeeze-blank ﹕遇到有連續兩行以上的空白行,就代換為一行的空白行 -v 或 --show-nonprinting cat -n textfile1 > textfile2 把 textfile1 的檔案內容加上行號後輸入 textfile2 這個檔案裡 cat -b textfile1 textfile2 >> textfile3 把 textfile1 和 textfile2 的檔案內容加上行號(空白行不加)之後將內容附加到 textfile3 將 file.txt 檔案清空, cat /dev/null > file.txt ## wc wc 可用來計算指定檔案內容的換行數(newline)、字數(word)與位元組數(byte),例如計算 /etc/motd 與 /etc/os-release 這兩個檔案的字數統計:  wc 跟一般的 Linux 指令工具一樣,亦可從標準輸入讀取資料: ```bash $ cat /etc/motd | wc 7 40 286 # 只計算換行數 $ wc -l /etc/motd 7 /etc/motd # 只計算字數 wc -w /etc/motd 40 /etc/motd # 只計算位元組數 wc -c /etc/motd 286 /etc/motd ``` ## 如何保持執行後視窗開啟 ### 1.文件的结尾,增加一个sleep 10000 ```bash= #!/bin/sh score=85 if [ $score -ge 90 ]; then echo "成績優秀" elif [ $score -ge 80 ]; then echo "成績良好" else echo "成績普通" fi sleep 10000 ``` ### 2.用上面的案例舉例,最後加入兩行指令,可以達到類似bat中的pause命令效果 ```bash= #!/bin/sh score=85 if [ $score -ge 90 ]; then echo "成績優秀" elif [ $score -ge 80 ]; then echo "成績良好" else echo "成績普通" fi echo 按任意鍵繼續 read -n 1 ``` ## $? 特殊變數 ### 在 Bash 中執行指令之後,可以透過 $? 這個特殊變數來取得前一個執行指令的傳回值,而我們可以透過這個數值來判斷前一個指令是否有執行成功。 ```bash= #!/bin/bash # 錯誤的指令 ls --wrong-opt # 判斷前一個指令是否執行成功 if [ $? -eq 0 ]; then echo "執行成功" exit 0 else echo "執行失敗" >&2 # 輸出至標準錯誤 exit 1 fi # 輸出結果: # ls: unrecognized option '--wrong-opt' # Try 'ls --help' for more information. # 執行失敗 ``` ## 出錯自動停止執行 ### 在執行 Bash 指令稿的時候,在一開始以 set 設定 -e 選項,就可以讓指令稿在出現任何錯誤時(執行指令傳回非 0 的值),自動停止執行。 ```bash= #!/bin/bash # 遇到任何錯誤則停止執行 set -e # 錯誤的指令 ls --wrong-opt # 後續的指令 echo "Hello, world." #輸出結果: # ls: unrecognized option '--wrong-opt' # Try 'ls --help' for more information. ``` ## exit後綴數字功能 ### exit命令同於退出shell,並返回給定值。在shell腳本中可以終止當前腳本執行。執行exit可使shell以指定的狀態值退出。若不設置狀態值參數,則shell以預設值退出。 <font color="#f00">狀態值0代表執行成功,其他值代表執行失敗。</font> ## Bash 與 Batch 差異 "Bash" 和 "Batch" 是兩種不同的腳本編程語言,用於執行命令和自動化任務的不同操作系統。以下是它們之間的主要差異: 1. 系統和環境: Bash(Bourne Again Shell)是一種常見於Unix或類Unix操作系統上的Shell腳本語言。它通常用於Linux、macOS等系統。 Batch 是一種Windows操作系統上的批處理腳本語言。它是一種特定於Windows的腳本語言。 2. 語法和語言特性: Bash 使用一種基於文本的Shell腳本語法,支援條件語句、迴圈、函數、變數等高級編程特性。它還具有強大的文本處理能力。 Batch 使用一種較簡單的批處理語言,其語法相對受限,主要用於執行一系列命令,而不具備如Bash那樣的高級編程功能。 3. 命令和工具: Bash 使用Unix或類Unix系統上的命令和工具,如ls、cp、rm等。 Batch 使用Windows命令行工具,如dir、copy、del等。 4. 可移植性: Bash 腳本通常在支援Unix或類Unix系統的多個平台上運行,可以具有較高的可移植性。 Batch 腳本主要在Windows上運行,不太適用於其他操作系統。 5. 默認Shell: Bash 通常是Linux和macOS上的默認Shell,因此Bash腳本在這些系統上非常常見。 Batch 腳本是Windows上的默認批處理語言。 雖然這兩種腳本語言都用於自動化任務和批處理,但它們主要面向不同的操作系統,具有不同的語法和功能。因此,您應根據您的操作系統和具體需求選擇使用Bash或Batch腳本。如果您需要在不同的操作系統上運行腳本,可能需要編寫不同版本的腳本以適應各個平台。 ## Powershell 與 Bash:差異 當你第一次學習如何使用 PowerShell 和 Bash 時,它們可能看起來很相似,但它們本質上是完全不同的野獸,其設計理念有時會發生衝突。 以下是使它們與眾不同的一些非常重要的事情: 1. 用戶訪問控制 如果你想使用 Bash 執行一些管理操作,你只需在命令前加上 sudo 即可。 命令完成後,你將立即返回到用戶 shell。 執行 root 命令可以減少出錯的空間,並迫使你檢查正在執行的操作。 另一方面,PowerShell 只能在管理模式或用戶模式下運行。 如果你想在它們之間切換,則必須打開一個新會話。 要以管理模式打開 PowerShell,你必須右鍵單擊程序圖標,然後單擊「以管理員身份運行」。 或者,在一個終端會話中,你只需鍵入 Start-Process powershell -verb runAs ,它將打開具有管理許可權的第二個會話。 2. 輸出 當你在 Bash 中鍵入命令時,你基本上是在運行一個單獨的程序,並且該可執行文件將為你提供你想要的結果。 輸出的格式取決於該特定程序的開發人員希望其如何格式化。 這裡有很大的靈活性,這很好,但缺點是缺乏統一性。 PowerShell 中的命令輸出通常按照一種統一標準進行格式化。 你在屏幕上看到的文本是人類可讀的數據版本,可以由其他程序解釋。 這使得編寫腳本和應用程序的人們只需使用 PowerShell 自己的功能和命令即可獲取他們需要的所有信息。 3. 語法 Bash 使用非常獨特的語法(例如,if [condition]; 然後在這裡編碼; fi) 這在其他腳本語言中通常不常見。 使用起來並不是非常困難,但對於剛剛開始學習如何編寫腳本的人來說可能有點不傳統。 至於用戶命令,沒有明確的統一性。 每個程序都有自己的命令語法。 在將它們集成到 bash 腳本中之前,你必須知道如何使用它們。 PowerShell 的腳本語法與任何其他.NET 應用程序類似(例如,if (condition) { …此處的代碼… })。 對於習慣在微軟自己的生態系統中進行開發的人來說,幾乎沒有學習曲線。 用戶命令遵循嚴格的結構:動詞,後跟達世幣,然後是名詞。 例如,Get-ChildItem 將列出當前目錄中的所有文件。 非 PowerShell 特定的命令仍然可以遵循類似於 Bash 的結構。 如果你在 Linux 上使用它,你仍然可以執行每個 Linux 命令。 4:靈活性 如果你掌握了 Bash,你就會知道如何在無數 Linux 發行版、BSD 生態系統、甚至 macOS 的終端環境中進行操作。 它是迄今為止的通用性之王。 儘管 macOS 在 2019 年改用了 zsh,但它的核心仍然是 Bash 精神。 PowerShell 幾乎可以在任何操作系統中運行,但它主要是與 Microsoft 自家產品一起運行。 如果你正在使用它,你很可能是在 Windows 中這樣做的。 如果你所學到的只是 PowerShell 特定的約定,那麼你實際上將被困在 Microsoft 產品的孤島上。 儘管情況並非一定如此,但無法迴避的事實是,外殼只有在這種情況下才會真正發光。 然而,就其缺點而言,它佔據了全球桌面市場的巨大份額。 ## xargs ```bash= $ git branch -a | grep -v master | xargs git branch -D Deleted branch a (was 42f678b). Deleted branch b (was 42f678b). Deleted branch c (was 42f678b). ```
×
Sign in
Email
Password
Forgot password
or
By clicking below, you agree to our
terms of service
.
Sign in via Facebook
Sign in via Twitter
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
New to HackMD?
Sign up