linux bash shell 學習

bash shell文件的開頭:


#!/bin/bash

一個簡單的bash例子,在屏幕中輸出Hello World

#!/bin/bash
 echo "Hello world!"

一段簡單的Shell

首先來一段簡單的Shell腳本,整體上對Shell有一個簡單的認識。

#!/bin/bash

cd ~ # 切換到用戶的主目錄,也就是/home/xxx
mkdir shell_test # 創建一個名爲shell_test的文件夾

for ((i=0; i<10; i++)) # Shell的for循環
do
    touch test_$i #創建空文件,文件名爲test_0, test_1 ...
done

上面是一段簡單的Shell程序,實現在功能爲:

  1. 在用戶主目錄下創建一個shell_test文件夾;
  2. 在用戶主目錄下創建10個規定命名格式的空文件。

由於是第一個Shell程序,所以有必要逐行解釋一下,留下一個好的印象。

  • 第一行:指定腳本解釋器,這裏是用/bin/bash做解釋器;
  • 第二行:切換到當前用戶的home目錄;
  • 第三行:創建一個名爲shell_test的空目錄;
  • 第四、五行:for循環,一共循環10次;
  • 第六行:創建10個名稱爲test0.txt, test1.txt格式的空文件。

cdmkdirtouch都是系統自帶的程序,一般在/bin目錄下;而fordodone都是Shell的關鍵字。 Shell中使用#開頭的行就是註釋。
一個簡單的程序之後,我們就開始進入Shell的語法學習吧。

變量

從變量開始學習這段旅程。

基礎知識
1. 在Shell中,使用變量之前不需要事先聲明,只是通過使用它們來創建它們;
2. 在默認情況下,所有變量都被看做是字符串,並以字符串來存儲;
3. Shell變量是區分大小寫的;
4. 在Shell中,通過在變量名前加一個$符號來訪問它的內容,無論何時想要獲取變量內容,都必須在它前面加一個$符號;

下面通過一段Shell腳本來詳細的說明上面的內容:

#!/bin/bash

var1=10 #定義一個變量var1

#定義一個變量var2,對於字符串中有空格的情況,需要使用引號將字符串括起來,
#此外等號兩邊不能有空格
var2="Jelly Think"

echo $var1 #獲取變量var1的值
echo $var2 #獲取變量var2的值

使用read
在Shell中,我們可以使用read命令將用戶的輸入賦值給一個變量。這個命令需要一個參數,即準備讀入用戶輸入數據的變量名,然後它會等待用戶輸入數據。通常情況下,在用戶按下回車鍵時,read命令結束。例如以下代碼:

#!/bin/bash

read var1 #讀入用戶輸入的var1變量的值
echo $var1 #輸出var1的值

exit 0

引號的使用技巧
在上面的代碼中也說了,如果字符串中包含了空格,就需要使用引號將字符串括起來,而這只是引號的一個簡單的使用。 像$var1這種的變量在引號中的行爲取決於你所使用的引號類型。這句話有以下兩層意思:

  1. 如果把一個$變量表達式放在雙引號中,程序執行到這一行時就會把變量替換爲它的值;
  2. 如果把一個$變量表達式放在單引號中,就不會發生替換現象;

但是,我們可以通過在$字符前面加上一個\字符取消它的特殊含義,也就是經常說的轉義字符。下面通過一段簡單的代碼來理解上面內容的意思:

#!/bin/bash

var1="JellyThink"

echo "This is $var1" #輸出:This is JellyThink
echo 'This is $var1' #輸出:This is $var1
echo "This is \$var1" #輸出:This is $var1

exit 0

環境變量
當一個Shell腳本程序開始執行時,一些變量根據環境設置中的值進行初始化,一般比較常用的有以下幾個:

  • $HOME:當前用戶的主目錄,例如:/home/jelly
  • $PATH:以冒號分隔的用來搜索命令的目錄列表;
  • $0:Shell腳本的名字;
  • $#:傳遞給腳本的參數個數;
  • $$:Shell腳本的進程號,腳本程序通常會用它來生成一個唯一的臨時文件。

參數變量
如果腳本程序在調用時帶有參數,一些額外的變量就會被創建。即使沒有傳遞任何參數,環境變量$#也依然存在,只不過它的值是0罷了。例如以下實力代碼:

#!/bin/bash

#參數的命名是$1、$2......
#$0表示腳本的名字

#輸出參數個數
echo "參數個數爲:$#"
echo "腳本名字:$0"
echo "第一個參數:$1"
echo "第二個參數:$2"

exit 0

輸出如下:

參數個數爲:2
腳本名字:./argsDemo.sh
第一個參數:Jelly
第二個參數:Think

BASH 中有一些保留變量,下面列出了一些:

$IFS  這個變量中保存了用於分割輸入參數的分割字符,默認識空格。 
$HOME  這個變量中存儲了當前用戶的根目錄路徑。 
$PATH  這個變量中存儲了當前 Shell 的默認路徑字符串。 
$PS1  表示第一個系統提示符。 
$PS2  表示的二個系統提示符。 
$PWD  表示當前工作路徑。 
$EDITOR 表示系統的默認編輯器名稱。 
$BASH  表示當前 Shell 的路徑字符串。
$0, $1, $2, ... 
表示系統傳給腳本程序或腳本程序傳給函數的第0個、第一個、第二個等參數。
$#   表示腳本程序的命令參數個數或函數的參數個數。
$$   表示該腳本程序的進程號,常用於生成文件名唯一的臨時文件。 
$?   表示腳本程序或函數的返回狀態值,正常爲 0,否則爲非零的錯誤號。
$*   表示所有的腳本參數或函數參數。
$@   和 $* 涵義相似,但是比 $* 更安全。
$!   表示最近一個在後臺運行的進程的進程號。

條件判斷

我們知道,所有程序設計語言的基礎是對條件進行測試判斷,並根據測試結果採取不同行動的能力。在總結之前,先看看Shell腳本程序裏可以使用的條件結構,然後再來看看使用這些條件的控制結構。

一個Shell腳本能夠對任何可以從命令行上調用的命令的退出碼進行判斷,這其中也包括我們寫的Shell腳本程序。這就是爲什麼要在所有自己編寫的腳本程序的結尾包括一條返回值的exit命令的重要原因。

test或[命令
在實際工作中,大多數腳本程序都會廣泛使用Shell的布爾判斷命令[test。在一些系統上,這兩個命令其實是一樣的,只是爲了增強可讀性,當使用[命令時,我們還使用符號]來結尾。寫了這麼多程序了,還是頭一次見,將一個[作爲一個命令。算我孤陋寡聞了。下面就通過一個簡單的例子來看看test[是如何使用的。

#!/bin/bash

if test -f testDemo.sh
then
    echo "testDemo.sh exists."
fi

if [ -f test.c ]
then
    echo "test.c exists."
else
    echo "I cannot find exists."
fi

exit 0

我們通過使用-f來判斷指定文件是否存在。test命令可以使用的條件類型可以歸爲3類:

  • 字符串比較;
  • 算術比較;
  • 文件相關的條件測試。

下面就分別看看。

字符串比較 結果
string1 = string2 如果兩個字符串相同,結果就爲真
string1 != string2 如果兩個字符串不同,結果就爲真
-n string 如果字符串不爲空,則結果爲真
-z string 如果字符串爲一個空串(null),則結果爲真
算術比較 結果
expression1 -eq expression2 如果兩個表達式相等,則結果爲真
expression1 -ne expression2 如果兩個表達式不等,則結果爲真
expression1 -gt expression2 如果expression1大於expression2,則爲真
expression1 -ge expression2 如果expression1大於等於expression2,則爲真
expression1 -lt expression2 如果expression1小於expression2,則爲真
expression1 -le expression2 如果expression1小於等於expression2,則爲真
!expression 表達式爲假,則結果就爲真;反之亦然
文件條件測試 結果
-d file 如果文件是一個目錄,則爲真
-f file 如果文件是一個普通文件,則爲真;也可以用來測試文件是否存在
-r file 如果文件可讀,則結果爲真
-s file 如果文件大小不爲0,則結果爲真
-w file 如果文件可寫,則結果爲真
-x file 如果文件可執行,則結果爲真

控制結構

Shell也有一組控制結構,它們與其它程序語言中的控制接口很相似,下面就分開來總結它們。

if語句
結構如下:

if condition
then
    statements
else
    statements
fi

代碼示例:

#!/bin/bash

var1=Jelly #這個是賦值,不能有空格分隔
var2=Think

if [ $var1 = $var2 ] #‘=’號兩邊都需要空格分隔
then
    echo "$var1 = $var2"
else
    echo "$var1 != $var2"
fi

exit 0

elif語句

結構如下

if condition
then
    statements
elif condition
then
    statements
else
    statements
fi

一個很容易出錯的問題。例如以下代碼:

#!/bin/bash

echo "Is it morning? please answer yes or no."
read timeofday

if [ $timeofday = "yes" ]
then
    echo "Good morning."
elif [ $timeofday = "no" ]
then
    echo "Good afternoon."
else
    echo "Sorry, $timeofday not recognized. Enter yes or no."
    exit 1
fi

exit 0

上面這段代碼很簡單,運行以下,如果你不輸入yesno,就會運行出錯,得到以下提示信息:

./elifDemo1.sh: line 6: [: =: unary operator expected
./elifDemo1.sh: line 9: [: =: unary operator expected

這是爲何?代碼中有if [ $timeofday = "yes" ],當我不輸入任何內容時,這個if語句就會變成這樣if [ = "yes" ],很明顯,這不是一個合法的條件。爲了避免出現這種情況,我們必須給變量加上引號,改成這樣if [ "$timeofday" = "yes" ]。這樣就沒有問題了。

for語句

結構如下:

for variable in values
do
    statements
done

代碼示例:

#!/bin/bash

for foo in Jelly Think Website
do
    echo $foo
done

exit 0

在文章的開頭,那段簡單的代碼中,也包含了for的簡單使用。

while語句

結構如下:

while condition
do
    statements
done

代碼示例:

#!/bin/bash

echo "Enter password: "
read pwd

while [ "$pwd" != "root" ]
do
    echo "Sorry, try again."
    read pwd
done

exit 0

until語句

結構如下:

until condition
do
    statements
done

它與while循環很相似,只是把條件測試反過來了;也就是說,循環將反覆執行直到條件爲真。

case語句

結構如下:

case variable in
    pattern [ | pattern] ...) statements;;
    pattern [ | pattern] ...) statements;;
    ...
esac

case的代碼結構相對來說是比較複雜的。case結構具備匹配多個模式,然後執行多條相關語句的能力,這使得它非常適合於處理用戶的輸入。下面通過一個實例來看看case的具體使用。

#!/bin/bash

echo "Is it morning? Please answer yes or no."
read input

case "$input" in
    yes ) echo "Good Morning.";;
    no ) echo "Good Afternoon.";;
    y ) echo "Good Morning.";;
    n ) echo "Good Afternoon.";;
    * ) echo "Sorry, answer not recognized";;
esac

exit 0

case語句被執行時,它會把變量input的內容與各字符串依次進行比較。一旦某個字符串與輸入匹配成功,case命令就會執行緊隨右括號)後面的代碼,然後就結束。 在代碼中,最後面的*表示匹配任何字符串,我們在寫代碼時,總是在其它匹配之後再添加一個*以確保如果沒有字符串得到匹配,case語句也會執行某個默認動作。 由於case比較複雜,所以不得不再來一段代碼,究其用法,如下:

#!/bin/bash

echo "Is it morning? Please answer yes or no."
read input

case "$input" in
    yes | y | Yes | YES ) echo "Good Morning.";;
    n* | N* ) echo "Good Afternoon.";;
    * ) echo "Sorry, answer not recognized.";;
esac

exit 0

上面這段代碼,使用了|符號,也就是說,也就是合併了多個匹配模式;同時還使用了*通配符;沒有問題,上面的代碼運行的很好。

&&和||操作符

Shell中也支持&&||符號,和C++語言中的是一樣;比如:

statement1 && statement2 && statement3 && ...

從左開始順序執行每條命令,如果一條命令返回的是true,它右邊的下一條命令才能執行。如果此持續直到有一條命令返回false,或者列表中的所有命令都執行完畢;遵循“短路”規則。

statement1 || statement2 || statement3 || ...

從左開始順序執行每條命令,如果一條命令返回的是false,它右邊的下一條命令才能夠被執行。如此持續直到有一條命令返回true,或者列表中的所有命令都執行完畢。

語句塊

在Shell中也有語句塊,使用{}來定義一個語句塊。我們可以把多條語句放到一個語句塊中執行。

函數

函數,這麼NB的東西,Shell怎麼能少呢。定義函數的結構如下:

function_name()
{
    statements
}

代碼示例:

#!/bin/bash

foo #運行到這裏,還沒有定義foo函數,會報錯

foo() {
    echo "Function foo is executing."
}

echo "script starting"
foo #輸出"Function foo is executing."
echo "script ended"

exit 0

腳本程序從自己的頂部開始執行,當它遇到了foo() {結構時,它知道腳本正在定義一個名爲foo的函數。當執行到單獨的foo時,Shell就知道應該去執行剛纔定義的函數了。函數執行完畢以後,腳本接着foo後的代碼繼續執行。 當一個函數被調用時,腳本程序的位置參數($*$@$#$1$2等)會被替換爲函數的參數,這也是我們讀取傳遞給函數的參數的方法。當函數執行完畢後,這些參數會恢復爲它們先前的值。 我們可以使用local關鍵字在Shell函數中聲明局部變量,局部變量將僅在函數的作用範圍內有效。此外,函數可以訪問全局作用範圍內的其它Shell變量。如果一個局部變量和一個全局變量的名字相同,前者就會覆蓋後者,但僅限於函數的作用範圍之內,例如以下代碼:

#!/bin/bash

sample_text="global variable"

foo()
{
    local sample_text="local variable"
    echo "Function foo is executing..."
    echo $sample_text
}

echo "script starting..."
echo $sample_text

foo

echo "script ended..."
echo $sample_text

exit 0

輸出如下:

script starting...
global variable
Function foo is executing...
local variable
script ended...
global variable

 用 BASH 設計簡單用戶界面

BASH 中提供了一個小的語句格式,可以讓程序快速的設計出一個字符界面的用戶交互選擇的菜單,該功能就是由 select 語句來實現的,select 語句的語法爲:

select var in

do
statments use $var
done

上面的語法結構在執行後,BASH 會將 中的所有項加上數字列在屏幕上等待用戶選擇,在用戶作出選擇後,變量 $var 中就包含了那個被選中的字符串,然後就可以對該變量進行需要的操作了。我們可以從下面的例子中更直觀的來理解這個功能:

#!/bin/bash 

OPTIONS="Hello Quit" 
select opt in $OPTIONS; do 
if [ "$opt" = "Quit" ]; then 
echo done 
exit 
elif [ "$opt" = "Hello" ]; then 
echo Hello World 
else 
clear 
echo bad option 
fi 
done 

exit 0<span style="font-family:Tahoma, Helvetica, Hei, SimSun, sans-serif;"><span style="font-size: 14px; line-height: 25.2px;">
</span></span>

發佈了27 篇原創文章 · 獲贊 67 · 訪問量 9萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章