快速學習Bash

做者:Vamei 出處:http://www.cnblogs.com/vamei 嚴禁轉載。html


Shell是Linux下經典的文本互動方式,而Bash是如今最經常使用的一種Shell。我在這裏總結了Bash的要點知識。程序員

 

Shell綜述

Linux圖形化桌面算不上精美。幸虧,Linux提供了更好的與樹莓派互動的方式:Shell。打開終端(Terminal),桌面上就會出現一個黑色背景的窗口,裏面就運行着一個Shell。若是你敲擊鍵盤,會發現字符會顯示在$提示符的後面,造成一串文本形式的命令。所謂的Shell,就是運行在終端中的文本互動程序。Shell分析你的文本輸入,而後把文本轉換成相應的計算機動做。express

 

在後面的內容中,我將用$來表示Linux系統Shell的命令提示符。好比說輸入date命令:編程

$date

date用於日期時間的相關功能。敲擊回車鍵Enter後,Shell會顯示出系統當前的時間。bash

 

Shell看起來簡陋,但實際上比圖形化桌面強大得多。它是Unix體系下的文本交互界面。你只須要用鍵盤來輸入文本,就能夠和操做系統交互。但這仍是不夠具體。說到底,Shell實際上是一個運行着的程序。這個程序接收到你按下回車鍵之間的輸入,就會對輸入的文本進行分析。好比下面這個命令:app

$free -h

包括空格在內總共7個字符。Shell程序會經過空格,區分出命令的不一樣部分。第一個部分是命令名。剩下的部分是選項和參數。在這個例子中,Shell會進一步分析第二個部分,發現這一部分的開頭是"-"字符,從而知道它是一個選項。less

 

有了命令名,Shell下一步就要執行該命令名對應的動做。這聽起來就像是在戲劇舞臺上,演員按照腳本演戲。Shell命令能夠分爲以下三類:運維

  • Shell內建函數(built-in function)
  • 可執行文件(executable file)
  • 別名(alias)

Shell的內建函數是Shell自帶的功能,而可執行文件是保存在Shell以外的腳本,提供了額外的功能。Shell必須在系統中找到對應命令名的可執行文件,才能正確執行。咱們能夠用絕對路徑來告訴Shell可執行文件所在的位置。若是用戶只是給出了命令名,而沒有給出準確的位置,那麼Shell必須自行搜索一些特殊的位置,也就是所謂的默認路徑。Shell會執行第一個名字和命令名相同的可執行文件。這就至關於,Shell幫咱們自動補齊了可執行文件的位置信息。咱們能夠經過which命令,來肯定命令名對應的是哪一個可執行文件:編程語言

$which date

 

別名是給某個命令一個簡稱,之後在Shell中就能夠經過這個簡稱來調用對應的命令。在Shell中,咱們能夠用alias來定義別名:編輯器

$alias freak="free -h"

Shell會記住咱們的別名定義。之後我在這個Shell中輸入命令freak時,都將等價於輸入free -h。

 

在Shell中,咱們能夠經過type命令來了解命令的類型。若是一個命令是可執行文件,那麼type將打印出文件的路徑。

$type date $type pwd

總的來講,Shell就是根據空格和其餘特殊符號,來讓電腦理解並執行用戶要求的動做。到了後面,咱們還將看到Shell中其餘的特殊符號。

 

Shell的選擇

Shell是文本解釋器程序的統稱,因此包括了不止一種Shell。常見的Shell有sh、bash、ksh、rsh、csh等。在樹莓派中,就安裝了sh和bash兩個Shell解釋器。sh的全名是Bourne Shell。名字中的玻恩就是這個Shell的做者。而bash的全名是Bourne Again Shell。最開始在Unix系統中流行的是sh,而bash做爲sh的改進版本,提供了更加豐富的功能。通常來講,都推薦使用bash做爲默認的Shell。樹莓派,以及其餘Linux系統中普遍安裝sh,都是出於兼容歷史程序的目的。

 

咱們能夠經過下面的命令來查看當前的Shell類型:

$echo $SHELL

echo用於在終端打印出文本。而$是一個新的Shell特殊符號。它提示Shell,後面跟隨的不是通常的文本,而是用於存儲數據的變量。Shell會根據變量名找到真正的文本,替換到變量所在的位置。SHELL變量存儲了當前使用的Shell的信息你能夠在bash中用sh命令啓動sh,並能夠用exit命令從中退出。

 

命令的選項和參數

咱們已經看到,一行命令裏還能夠包含着選項和參數。總的來講,選項用於控制命令的行爲,而參數說明了命令的做用對象。好比說:

$uname -m

 

在上面的命令中,選項-m影響了命令uname的行爲,致使uname輸出了樹莓派的CPU型號。若是不是該選項的影響,uname輸出的將是"Linux"。咱們不妨把每一個命令看作多功能的瑞士軍刀,而選項讓命令在不一樣的功能間切換。由一個"-"引領一個英文字母,這成爲短選項。多個短選項的字母能夠合在一塊兒,跟在同一個"-"後面。好比,下面的兩個命令就等價:

$uname -m -r $uname -mr

 

此外還有一種長選項,是用"--"引領一整個英文單詞,好比:

$date --version

上面的命令將輸出date程序的版本信息。

 

若是說選項控制了瑞士軍刀的行爲,那麼參數就提供了瑞士軍刀發揮用場的原材料。就拿echo這個命令來講,它能把字符打印到終端。它選擇打印的對象,正是它的參數:

$echo hello

 

有的時候,選項也會攜帶變量,以便來講明選項行爲的原材料。好比:

$sudo date --set="1999-01-01 08:00:00"

選項"--set"用於設置時間,用等號鏈接的,就是它的參數。date會把日期設置成這一變量所表明的日期。若是用短選項,那麼就要用空格取代等號了:

$sudo date -s "1999-01-01 08:00:00"

值得注意的是,Shell對空格敏感。當一整個參數信息中包含了空格時,咱們須要用引號把參數包裹起來,以便Shell能識別出這是一個總體。

 

所謂的選項和參數提供給命令的附加信息。所以,命令最終會拿這些字符串作什麼,是由命令本身決定的。所以,有時會發現一些特異的選項或參數用法。這個時候,你就要從文檔中尋找答案。

 

變量

咱們能夠在Bash中輸入一行的命令。Bash會把輸入的命令轉化爲特定的動做。從這一節起,咱們將看到Bash的可編程性。Bash提供了某些相似於C語言那樣的編程語法,從而容許你用編程的方式,來組合使用Linux系統。咱們首先看Bash用變量存儲數據的能力。正如咱們在C語言中看到的,變量是內存中的一起空間,能夠用於存儲數據。咱們能夠經過變量名來引用變量中保持的數據。藉助變量,程序員能夠複用出現過的數據。Bash中也有變量,但Bash的變量只能存儲文本。

 

1)變量賦值

Bash和C相似,一樣用「=」來表示賦值。好比:

$var=World

就是把文本World存入名爲var的變量,即賦值。根據Bash的語法,賦值符號「=」的先後不留空格。賦值號右邊的文本內容會存入賦值號左邊的變量。

 

若是文本中包含空格,那麼你能夠用單引號或雙引號來包裹文本。好比:

$var='abc bcd'

或者:

$var="abc bcd"

 

在Bash中,咱們能夠把一個命令輸出的文本直接賦予給一個變量:

$now=`date`

藉助``符號,date命令的輸出存入了變量now。

 

咱們還能夠把一個變量中的數據賦值給另外一個變量:

$another=$var

  

2)引用變量

咱們能夠用$var的方式來引用變量。在Bash中,所謂的引用變量就是把變量翻譯成變量中存儲的文本。好比:

$var=World $echo $var

 就會打印出World,即變量中保存的文本。

 

在Bash中,你還能夠在一段文本中嵌入變量。Bash也會把變量替換成變量中保存的文本。好比: 

$echo Hello$var

 文本將打印出HelloWorld。

 

爲了不變量名和尾隨的普通文本混淆,咱們也能夠換用${}的方式來標識變量。好比說:

$echo $varIsGood

 因爲Bash中並無varIsGood這個變量,因此Bash將打印空白行。但若是將命令改成: 

$echo ${var}IsGood

 Bash經過${}識別出變量var,並把它替換成數據。最終echo命令打印出WorldIsGood。

 

在Bash中,爲了把一段包含空格的文本當作單一參數,就須要用到單引號或雙引號。你能夠在雙引號中使用變量。好比:

$echo "Hello $var"

 將打印Hello World。與此相對,Bash會忽視單引號中的變量引用,因此單引號中的變量名只會被當作普通文本,好比:

$echo 'Hello $var'

 將打印Hello $var。

 

數學運算

在Bash中,數字和運算符都被當作普通文本。因此你沒法像C語言同樣便捷地進行數學運算。好比執行下面的命令:

$result=1+2 $echo $result

Bash並不會進行任何運算。它只會打印文本「1+2」。

 

在Bash中,你還能夠經過$(())語法來進行數值運算。在雙括號中你能夠放入整數的加減乘除表達式。Bash會對其中的內容進行數值運算。好比

$echo $((2 + (5*2)))

 將打印運算結果12。此外,在$(())中,你也可使用變量。好比: 

$var=1 $echo $(($var + (5*2)))

 將打印運算結果11。

 

你能夠用Bash實現多種整數運算:

 加法:$(( 1 + 6 ))。結果爲7。
 減法:$(( 5 – 3 ))。結果爲2。
 乘法:$(( 2*2 ))。結果爲4。
 除法:$(( 9/3 ))。結果爲3。
 求餘:$(( 5%3 ))。結果爲2。
 乘方:$(( 2**3 ))。結果爲8。

 

如今,你就能夠把數學運算結果存入變量:

$result=$(( 1 + 2 ))

 

 

返回代碼

在Linux中,每一個可執行程序會有一個整數的返回代碼。按照Linux慣例,當程序正常運行完畢並返回時,將返回整數0。所以,C程序中返回0的語句,都出如今C程序中main函數的最後一句。例以下面的foo.c程序:

int main(void) { int a; int b; int c; a = 6; b = 2; c = 6/2; return 0; }

 這段程序能夠正常運行。所以,它將在最後一句執行return語句,程序的返回代碼是0。在Shell中,咱們運行了程序後,能夠經過$?變量來獲知返回碼。好比:

$gcc foo.c $./a.out $echo $?

 

若是一個程序運行異常,那麼這個程序將返回非0的返回代碼。好比刪除一個不存在的文件: 

$rm none_exist.file $echo $?

 

 在Linux中,能夠在一個行命令中執行多個程序。好比:

$touch demo.file; ls;

 

在執行多個程序時,咱們可讓後一個程序的運行參考前一個程序的返回代碼。好比說,只有前一個程序返回成功代碼0,才讓後一個程序運行: 

$rm demo.file && echo "rm succeed"

若是rm命令順利運行,那麼第二個echo命令將執行。

 

還有一種狀況,是等到前一個程序失敗了,才運行後一個程序,好比:

$rm demo.file || echo "rm fail"

若是rm命令失敗,第二個echo命令纔會執行。

 

Bash腳本

你還能夠把多行的Bash命令寫入一個文件,成爲所謂的Bash腳本。當Bash腳本執行時,Shell將逐行執行腳本中的命令。編寫Bash腳本,是咱們開始實現Bash代碼複用的第一步。

 

1)腳本的例子

用文本編輯器編寫一個Bash腳本hello_world.bash: 

#!/bin/bash echo Hello echo World

 

腳本的第一行說明了該腳本使用的Shell,即/bin/bash路徑的Bash程序。腳本正文是兩行echo命令。運行腳本的方式和運行可執行程序的方式相似,都是: 

$./hello_world.bash

須要注意的是,若是用戶不具備執行Bash腳本文件的權限,那麼他將沒法執行Bash腳本。此時,用戶必須更換文件權限,或者以其餘身份登陸,才能執行腳本。當腳本運行時,兩行命令將按照由上至下的順序依次執行。Shell將打印兩行文本:

Hello

World

 

Bash腳本是一種複用代碼的方式。咱們能夠用Bash腳本實現特定的功能。因爲該功能記錄在腳本中,所以我能夠反覆地運行同一個文件來實現相同的功能,而不是每次想用的時候都要從新敲一遍命令。咱們看一個簡單的Bash腳本hw_info.bash,它將計算機的信息存入到名爲log的文件中:

#!/bin/bash echo "Information of Vamei's computer:" > log lscpu >> log uname –a >> log free –h >> log

  

2)腳本參數

和可執行程序相似,Bash腳本運行時,也能夠攜帶參數。這些參數能夠在Bash腳本中以變量的形式使用。好比test_arg.bash:

#!/bin/bash echo $0
echo $1
echo $2

 

在Bash中,你能夠用$0、$一、$2……的方式,來得到Bash腳本運行時的參數。咱們用下面的方式運行Bash腳本:

$./test_arg.bash hello world

$0是命令的第一部分,也就是./test_arg.bash。$1表明了參數hello,而$2表明了參數world。所以,上面程序將打印:

./test_arg.bash

hello

world

 

若是變動參數,同一段腳本將有不一樣的行爲。這大大提升了Bash腳本的靈活性。上面的hw_info.bash腳本中,咱們把輸出文件名寫死成log。咱們也能夠修改腳本,用參數做爲輸出文件的文件名:

#!/bin/bash echo "Information of Vamei's computer:" > $1 lscpu >> $1
uname –a >> $1
free –h >> $1

 

藉助參數,咱們就能夠自由地設置輸出文件的名字:

$./hw_info.bash output.file

 

3)腳本的返回代碼

和可執行程序相似,腳本也能夠有返回代碼。仍是按照慣例,腳本正常退出時返回代碼0。在腳本的末尾,咱們能夠用exit命令來設置腳本的返回代碼。咱們修改hello_world.bash:

#!/bin/bash echo Hello echo World exit 0

其實在腳本的末尾加一句exit 0並沒必要要。一個腳本若是正常運行完最後一句,會自動的返回代碼0。在腳本運行後,咱們能夠經過$?變量查詢腳本的返回代碼: 

$./hello_world.bash $echo $?

 

若是在腳本中部出現exit命令,腳本會直接在這一行中止,並返回該exit命令給出的返回代碼。好比下面的demo_exit.bash:

#!/bin/bash echo hello exit 1
echo world

你能夠運行該腳本,檢查其輸出結果,並查看其返回代碼。

 

函數

在Bash中,腳本和函數有不少類似的地方。腳本實現了一整個腳本文件的程序複用,而函數複用了腳本內部的部分程序。一個函數能夠像腳本一個包含多個指令,用於說明該函數若是被調用會執行哪些活動。在定義函數時,咱們須要花括號來標識函數包括的部分: 

#!/bin/bash function my_info (){ lscpu >> log uname –a >> log free –h >> log } my_info

  

腳本一開始定義了函數my_info,my_info是函數名。關鍵字function和花括號都提示了該部分是函數定義。所以,function關鍵字並非必須的。上面的腳本等效於:

#!/bin/bash my_info (){ lscpu >> log uname –a >> log free –h >> log } my_info

花括號中的三行命令,就說明了函數執行時須要執行的命令。須要強調的是,函數定義只是食譜,並無轉化成具體的動做。腳本的最後一行是在調用函數。只有經過函數調用,函數內包含的命令才能真正執行。調用函數時,只須要一個函數名就能夠了。

 

像腳本同樣,函數調用時還能夠攜帶參數。在函數內部,咱們一樣能夠用$一、$2這種形式的變量來使用參數:

#!/bin/bash function my_info (){ lscpu >> $1
uname –a >> $1
free –h >> $1 } my_info output.file my_info another_output.file

在上面的腳本中,進行了兩次函數調用。函數調用時,分別攜帶了參數output.file和another_output.file。

 

跨腳本調用

在Bash中使用source命令,能夠實現函數的跨腳本調用。命令source的做用是在同一個進程中執行另外一個文件中的Bash腳本。好比說,有兩個腳本,my_info.bash和app.bash。腳本my_info.sh中的內容是: 

#!/bin/bash function my_info (){ lscpu >> $1
uname –a >> $1
free –h >> $1 }

 

腳本app.bash中的內容是: 

#!/bin/bash source my_info.bash my_info output.file

運行app.bash時,執行到source命令那一行時,就會執行my_info.bash腳本。在app.bash的後續部分,就可使用my_info.bash中的my_info函數。

 

 

邏輯判斷

咱們已經介紹了函數和腳本兩種組合命令的方式。這兩種方式均可以把多行命令合併起來,組成一個功能單元。函數和腳本都實現了必定程度的代碼複用。從這一節起,咱們將看到選擇和循環兩種語法結構,這兩種語法結構能夠改變腳本的運行順序,從而編寫出更加靈活的程序。Bash除了能夠進行數值運算,還能夠進行邏輯判斷。邏輯判斷是決定某個說法的真假。咱們在生活中很天然地進行各類各樣的邏輯判斷。好比「3大於2」這個說法,咱們會說它是真的。邏輯判斷就是對一個說法判斷真假。在Bash中,咱們能夠用test命令來進行邏輯判斷:

$test 3 -gt 2; echo $?

命令test後面跟有一個判斷表達式,其中的-gt表示大於,即greater than。因爲「3大於2」這一表達式爲真,因此命令的返回代碼將是0。若是表達式爲1,那麼命令的返回代碼是1:

$test 3 -lt 2; echo $?

表達式中的-lt表示小於,即less than。

 

數值大小和相等關係的判斷,是最多見的邏輯判斷。除了上面的大於和小於判斷,咱們還能夠進行如下的數值判斷:

  • 等於: $test 3 -eq 3; echo $? 
  • 不等於: $test 3 -ne 1; echo $? 
  • 大於等於: $test 5 -ge 2; echo $? 
  • 小於等於: $test 3 -le 1; echo $? 

 

Bash中最多見的數據形式是文本,所以也提供了不少關於文本的判斷:

  • 文本相同: $test abc = abx; echo $? 
  • 文本不一樣: $test abc != abx; echo $? 
  • 按照詞典順序,一個文本在另外一個文本以前: $test apple > tea; echo $? 
  • 按照詞典順序,一個文本在另外一個文本以後: $test apple < tea; echo $? 

 

Bash還能夠對文件的狀態進行邏輯判斷:

  • 檢查一個文件是否存在: $test –e a.out; echo $? 
  • 檢查一個文件是否存在,並且是普通文件: $test –f file.txt; echo $? 
  • 檢查一個文件是否存在,並且是目錄文件: $test –d myfiles; echo $? 
  • 檢查一個文件是否存在,並且是軟鏈接: $test –L a.out; echo $? 
  • 檢查一個文件是否可讀: $test –r file.txt; echo $? 
  • 檢查一個文件是否可寫: $test –w file.txt; echo $? 
  • 檢查一個文件是否可執行: $test –x file.txt; echo $? 

 

在作邏輯判斷時,能夠把多個邏輯判斷條件用「與、或、非」的關係組合起來,造成複合的邏輯判斷。 

! expression expression1 –a expression2 expression1 –o expression2

 

 

選擇結構

邏輯判斷能夠得到計算機和進程的狀態。進一步,Bash能夠根據邏輯判斷,讓程序有條件地運行,這也就是所謂的選擇結構。選擇結構是一種語法結構,可讓程序根據條件決定執行哪一部分的指令。最先的程序都是按照指令順序依次執行。選擇結構打破了這一順序,給程序帶來更高的靈活性。最簡單的,咱們能夠根據條件來決定是否執行某一部分程序,好比下面的demo_if.bash腳本:

#!/bin/bash var = `whoami` if [ $var = "root" ] then
 echo "You are root"
 echo "You are my God."
fi

這個腳本中使用了最簡單的if結構。關鍵字if後面跟着[],裏面是一個邏輯表達式。這個邏輯表達式就是if結構的條件。若是條件成立,那麼if將執行then到fi之間包含的語句,咱們稱之爲隸屬於then的代碼塊。若是條件不成立,那麼then的代碼塊不執行。這個例子的條件是判斷用戶是否爲root。所以,若是是非root用戶執行該腳本,那麼Shell不會打印任何內容。

 

咱們還能夠經過if...then...else...結構,讓Bash腳本從兩個代碼塊中選擇一個執行。該選擇結構一樣有一個條件。若是條件成立,那麼將執行then附屬的代碼塊,不然執行else附屬的代碼塊。下面的demo_if_else.bash腳本是一個小例子:

#!/bin/bash filename=$1
if [ -e $filename ] then
 echo "$filename exists"
else
 echo "$filename NOT exists"
fi

echo "The End"

 

if後面的「-e $filename」做爲判斷條件。若是條件成立,即文件存在,那麼執行then部分的代碼塊。若是文件不存在,那麼腳本將執行else語句中的echo命令。末尾的fi結束整個語法結構。腳本繼續以順序的方式執行剩餘內容。運行腳本: 

$./demo_if_else.bash a.out

腳本會根據a.out是否存在,打印出不一樣的內容。

 

咱們看到,在使用if...then...else...結構時,咱們能夠實現兩部分代碼塊的選擇執行。而在then代碼塊和else代碼塊內部,咱們能夠繼續嵌套選擇結構,從而實現更多個代碼塊的選擇執行。好比腳本demo_nest.bash:

#!/bin/bash var=`whoami` echo "You are $var"

if [ $var = "root" ] then
 echo "You are my God."
else
 if [ $var = "vamei" ]  then
 echo "You are a happy user."
 else
 echo "You are the Others."
 fi
fi

 

在Bash下,咱們還能夠用case語法來實現多程序塊的選擇執行。好比下面的腳本demo_case.bash: 

#!/bin/bash var=`whoami` echo "You are $var"

case $var in root) echo "You are God." ;; vamei) echo "You are a happy user." ;; *) echo "You are the Others." ;; esac

這個腳本和上面的demo_nest.bash功能徹底相同。能夠看到case結構與if結構的區別。關鍵字case後面再也不是邏輯表達式,而是一個做爲條件的文本。後面的代碼塊分爲三個部分,都以文本標籤)的形式開始,以;;結束。在case結構運行時,會逐個檢查文本標籤。當條件文本和文本標籤能夠對應上時,Bash就會執行隸屬於該文本標籤的代碼塊。若是是用戶vamei執行該Bash腳本,那麼條件文本和vamei標籤對應上,腳本就會打印:

You are a happy user.

 

文本標籤除了是一串具體的文本,還能夠包含文本通配符。結構case中經常使用的通配符包括:

 

通配符 含義 文本標籤例子 經過的條件文本
* 任意文本 *) Xyz, 123, …
? 任意一個字符 a?c) abc, axc, …
[] 範圍內一個字符 [1-5][b-d]) 2b, 3d, …

上面的程序中最後一個文本標籤是通配符*,即表示任意條件文本均可以觸發此段代碼塊的運行。固然,前提是前面的幾個文本標籤都沒有「截胡」。

 

循環結構

循環結構是編程語言中另外一種常見的語法結構。循環結構的功能是重複執行某一段代碼,直到計算機的狀態符合某一條件。在while語法中,Bash會循環執行隸屬於while的代碼塊,直到邏輯表達式不成立。好比下面的demo_while.bash:

#!/bin/bash now=`date +'%Y%m%d%H%M'` deadline=`date --date='1 hour' +'%Y%m%d%H%M'` while [ $now -lt $deadline ] do
 date
 echo "not yet"
 sleep 10 now=`date +'%Y%m%d%H%M'` done

echo "now, deadline reached"

關鍵字do和done之間的代碼是隸屬於該循環結構的代碼塊。在while後面跟着條件,該條件決定了代碼塊是否重複執行下去。這個條件是用當前的時間與目標時間對比。若是當前時間小於目標時間,那麼代碼塊就會重複執行下去。不然,Bash將跳出循環,繼續執行後面的語句。

 

若是while的條件始終是真,那麼循環會一直進行下去。下面的程序就是以無限循環的形式,不斷播報時間: 

#!/bin/bash while true 
do
  date
  sleep 1
done

 

語法while的終止條件是一個邏輯判斷。若是在循環過程當中改變邏輯判斷的內容,那麼咱們很難在程序執行以前預判循環進行的次數。正如咱們以前在demo_while.bash中看到的,咱們在循環進行過程當中改變着做爲條件的邏輯表達式,不斷地更新參與邏輯判斷的當前時間。與while語法對應的是for循環。這種語法會在程序進行前肯定好循環進行的次數,好比demo_for.bash: 

#!/bin/bash for var in `ls log*` do
  rm $var done

 

在這個例子中,命令ls log*將返回全部以log開頭的文件名。這些文件名之間由空格分隔。循環進行時,Bash會依次取出一個文件名,賦值給變量var,並執行do和done之間隸屬於for結構的程序塊。因爲ls命令返回的內容在是肯定的,所以for循環進行的次數也會在一開始肯定下來。

 

在for語法中,咱們也可使用本身構建一個由空格分隔的文本。由空格區分出來的每一個子文本會在循環中賦值給變量。好比:

#!/bin/bash for user in vamei anna yutian do
 echo $user done

 

此外,for循環還能夠和seq命令配合使用。命令seq用於生成一個等差的整數序列。命令後面能夠跟3個參數,第一個參數表示整數序列的開始數字,第二個參數表示每次增長多少,最後一個參數表示序列的終點。所以,下面命令: 

$seq 1 2 10

將返回:

1 3 5 7 9

能夠看到,seq返回的也是由空格分隔開的文本。所以,seq的返回結果也可用於for循環。

 

結合for循環和seq命令,咱們能夠解一些有趣的數學問題。好比高斯求和,是要計算從1到100的全部整數的和。咱們能夠用Bash解決: 

#!/bin/bash total=0

for number in `seq 1 1 100` do total=$(( $total + $number )) done

echo $total

 

 這個問題還能夠用do while循環來求解:

#!/bin/bash total=0 number=1
while : do
  if [ $number -gt 100 ] then break fi total=$(( $total + $number )) number=$(($number + 1)) done

echo $total

這裏break語句的做用是在知足條件時跳出循環。

 

若是想計算1到100全部不被3整數的和,則可使用continue語句,跳過全部被3整數的數:

#!/bin/bash total=0
for number in `seq 1 1 100` do
  if (( $number % 3 == 0 )) then continue fi total=$(( $total + $number )) done

echo $total

 

Bash與C語言

到了這裏,咱們已經介紹完Bash語言的基本語法。Bash語言和C語言都是Linux下的經常使用語言。它們都能經過特定的語法來編寫程序,而程序運行後都能實現某些功能。儘管在語法細節上存在差別,但兩種語言都有如下語法:

  • 變量:在內存中儲存數據
  • 循環結構:重複執行代碼塊
  • 選擇結構:根據條件執行代碼塊
  • 函數:複用代碼塊

編程語言的做者在設計語言時,每每會借鑑已有編程語言的優勢。這是編程語言之間類似性的一大緣由。程序員每每要掌握不止一套編程語言。類似的語法特徵,會讓程序員在學習新語言時感到親切,從而促進語言的推廣。

Bash和C的類似性,也來自於它們共同遵照的編程範式——面向過程編程。支持面向過程編程的語言,通常都會提供相似於函數的代碼封裝方式。函數把多行指令包裝成一個功能。只要知道了函數名,程序能夠經過調用函數來使用函數功能,最終實現代碼複用。除了面向過程編程,還有面向對象和函數式的編程範式。每種編程範式都提供了特定的代碼封裝方式,並達到代碼複用的目的。值得注意的是,近年來出現的新語言每每會支持不止一種編程範式。

除了類似性,咱們還應該注意到Bash和C程序的區別。Bash的變量只能是文本類型,C的變量卻能夠有整數、浮點數、字符等類型。Bash的不少功能,如加減乘除運算,都是調用其餘程序實現的。而C直接就能夠進行加減乘除運算。能夠說,C語言是一門真正的編程語言。C程序最終會編譯成二進制的可執行文件。CPU能夠直接理解這些文件中的指令。

另外一方面,Bash是一個Shell。它本質上是一個命令解釋器程序,而不是編程語言。用戶能夠經過命令行的方式,來調用該程序的某些功能。所謂的Bash編程,只是命令解釋器程序提供的一種互動方法。Bash腳本只能和Bash進程互動。它不能像C語言同樣,直接調用CPU的功能。所以,Bash能實現的功能會受限,運行速度上也比不上可執行文件。

但另外一反面,Bash腳本也有它的好處。 C語言能接觸到很底層的東西,但使用起來也很複雜。有時候,即便你已經知道如何用C實現一個功能,寫代碼依然是一個很繁瑣的過程。Bash正相反。因爲Bash能夠便捷地調用已有的程序,所以不少工做能夠用數行的腳本解決。此外,Bash腳本不須要編輯,就能夠由Bash進程理解並執行。所以,開發Bash腳本比寫C程序要快不少。Linux的系統運維工做,如按期備份、文件系統管理等,就常用到Bash腳本。總之,Bash編程知識是晉級爲資深Linux用戶的必要條件。

 

歡迎閱讀「騎着企鵝採樹莓」系列文章

相關文章
相關標籤/搜索