Linux Shell——流程控制


Linux Shell

一. 創建交互式腳本

使用 echo命令的選項

關於各種命令的使用,可以使用man 命令來查看命令的詳細用法介紹。例如,我想看下 echo 的用法和各種選項。可以執行 man echo。執行結果如下:

echo

如果單獨執行 echo 命令,就會打印出一個空白行。
默認情況下,echo 都會換行,如果不想換行的話,可以使用下面兩種方式的一種。

echo -n "Which directory do you want to use? "
echo -e "Which directory do you want to use? \c"

使用 read 命令

如果我們需要讀入用戶輸入的參數時,可以使用 read 命令,當然還可以從文件系統等讀入信息。
我們建立一個新的腳本文件 hello3.sh。

#!/bin/bash
echo -n "Hello I  $(basename $0) may I ask your name: "
read
echo "Hello $REPLY"
exit 0

執行此腳本時,首先提示需要輸入,然后輸入的內容,REPLY當沒有給read提供參數時設置,最后打印出來。

執行結果為:

hello3.sh

優化 read 提示的腳本

在前面的例子中,我們使用了echo -n的方式來阻止信息換行,其實 read 命令也帶了一個選項來實現相同的功能:

read -p "Enter your name: " name

上面的腳本中,我們定義了一個變量name用來保存輸入的內容,如果不定義變量的話,輸入的內容會保存在REPLY中。
下面是具體使用的語法:
read -p

hello3.sh的腳本可以改成如下:

#!/bin/bash
read -p "May I ask your name: " name
echo "Hello $name"
exit 0

限制輸入內容的個數

我們還可以使用 read命令的-n 選項,此選項后面需要接一個數字,可以限制輸入內容的個數。

#!/bin/bash
read -p "May I ask your name: " name
echo "Hello $name"
read -n1 -p "Press any key to exit"
echo
exit 0

控制輸入內容的可見性

目前,我們輸入的內容都是可見的,但有些敏感的數據,如密碼,信用卡號等信息,輸入時並不想可見。那么可以使用read -s

read -s

這時再輸入時,就有一個鑰匙的標識,而且輸入時不可見。

實例演示

現在有一個小程序,要求把指定后綴的文件備份到指定的目錄下,代碼如下:

#!/bin/bash
# Script to prompt to back up files and location
# The files will be search on from the user's home
# directory and can only be backed up to a directory within $HOME

read -p "Which file types do you want to backup " file_suffix
read -p "Which directory do you want to backup to " dir_name

# The next lines creates the directory if it does not exist
test -d $HOME/$dir_name || mkdir -m 700 $HOME/$dir_name

# The find command will copy files the match the
# search criteria ie .sh . The -path, -prune and -o
# options are to exclude the back directory from the
# backup.
find $HOME -path $HOME/$dir_name -prune -o \
 -name "*$file_suffix" -exec cp {} $HOME/$dir_name/ \;

exit 0

二. 條件判斷語句

使用test 內建函數

test命令是 shell 環境中用於測試條件表達式的工具。
它的返回值可以是 true,false,0或1。基本語法為:

test EXPRESSION

如果我們需要多個表達式,可以使用ANDOR,對應的選項為-a-o

test EXPRESSION -a EXPRESSION
test EXPRESSION -o EXPRESSION

其實,在實際應用中,test 有種非常簡潔的方式,就是被中括號包含的條件表達式。這種方式更加常用。語法如下:

[ EXPRESION ]

test 的用法很多,例如,可以比較兩個字符串是否相等:

[ $USER = root ]

我們還可以test 的選項來判斷一個字符串的長度是否為0。

[ -z $1 ]

上面的代碼的含義是,如果沒有參數輸入的話,返回 true。

test 還可以用來比較數字的大小。例如:

[ $# -gt 0 ]

除此而外,還可以判斷文件的類型。例如,我們想查找類型為符號鏈接的文件,然后刪除。可以使用如下腳本:

# [ -h $HOME/bin ] &&rm $HOME/bin

其他常用的選項如下:

  • -d:文件是否為目錄
  • -e:文件是否存在
  • -x:文件是否可以執行
  • -f:文件是否為常規文件
  • -r:文件是否可讀
  • -p:文件是否為命名管道
  • -b:文件是否為塊設備文件
  • -c:文件是否為字符設備文件

使用 if 條件判斷語句

if 語句的基本格式為:

if conditon ; then
    statement 1
    statement 2
fi

我們可以看一個具體的例子:

#!/bin/bash
if [ $# -lt 1 ] ; then
    echo "Usage: $0 <name>"
    exit 1
fi

echo "Hello $1"

exit 0

在運行此腳本時,如果沒有輸入參數,則提示正確的使用方法,非正常退出;否則,打印輸入的參數,程序正常退出。

if 語句還可以使用 else 條件分支。具體的語法為:

if conditon ; then
    statement
else
    statement
fi

我們新建一個腳本文件 hello6.sh,來展示 if else 的用法。

#!/bin/bash
if [ $# -lt 1 ] ; then
    read -p "Enter a name: "
    name=$REPLY
else
    name=$1
fi

echo "Hello $name"

exit 0

當沒有輸入參數時,程序提示需要輸入一個 name;如果輸入了的話,就會打印出來。

最后一種 if 的情況,也是最完整的 if 條件語句。語法如下;

if condition; then
    statement
elif condition; then
    statement
else
    statement
fi

再舉個例子,新建腳本 backup2.sh,我們使用 tar 命令來備份壓縮指定的目錄,根據輸入的參數“H”,“M”,“L”,可以執行不同的壓縮級別。

如果輸入的參數為“H”,則使用 bzip2的壓縮方式;

如果輸入的參數為“M”,則使用 gzip 的壓縮方式;

如果輸入的參數為“L”,則使用 tar 命令對文件打包,不壓縮。

#!/bin/bash
# this sciprt dirs’ location are defualt under $HOME

read -p "Choose H, M or L compression " file_compression
read -p "Which directory do you want to backup to " dir_name
read -p "Which directory do you went to backup: " tobe_backup_name

# The next lines creates the directory if it does not exist
test -d $HOME/$dir_name || mkdir -m 700 $HOME/$dir_name
backup_dir=$HOME/$dir_name

tar_l="-cvf  $backup_dir/b.tar $HOME/$tobe_backup_name"
tar_m="-czvf $backup_dir/b.tar.gz $HOME/$tobe_backup_name"
tar_h="-cjvf $backup_dir/b.tar.bzip2 $HOME/$tobe_backup_name"

if [ $file_compression = "L" ] ; then
    tar_opt=$tar_l
elif [ $file_compression = "M" ]; then
    tar_opt=$tar_m
else
    tar_opt=$tar_h
fi

tar $tar_opt

exit 0

使用 case 選擇語句

如果 if else 分支太多的話,可以考慮使用 case 語句,case 語句提供了更加簡潔的機制。
case 語句的語法結構為:

case expression in
    case1) 
        statement1
        statement2
    ;;
    case2)
        statement1
        statement2
   ;;
   *)
       statement1
   ;;
esac

還是舉個簡單的例子,新建腳本文件 grade.sh。

#!/bin/bash
# Script to evaluate grades
# Usage: grade.sh studentName grade

if [ ! $# -eq2 ] ; then
    echo "You must provide <studentName> <grade>
    exit 2
fi

case $2 in
    [A-C]|[a-c]) 
        echo "$1 is a star pupil"
    ;;
    [Dd]) 
        echo "$1 needs to try a little harder!"
    ;;
    [E-F]|[e-f]) 
        echo "$1 could do a lot better next year"
    ;;
    *) 
        echo "Grade could not be evaluated for $1"
esac

exit 0

運行結果如下:

grade

三. 一些注意事項

我們前面已經介紹過 test 內置命令用來判斷條件語句,但通常我們會推薦使用[]來替代 test 命令。例如:

[ -f /etc/hosts -a -r /etc/hosts ]

但這里面有個問題,例如,下面的腳本中,文件名中有空格,我們都知道,空格在命令行中有特殊的用處,用來分割命令選項和參數等。下面的代碼會報錯。

FILE="my file"
[ -f $FILE -a -r $FILE ] && cat $FILE

報錯信息為“too many arguments”,原因是程序把帶有空格的文件名解析成了兩部分。
而這時,我們需要把變量用雙引號包含起來,

FILE="my file"
[ -f "$FILE" -a -r "$FILE" ] && cat "$FILE"

那有沒有好的辦法可以不使用雙引號呢?當然有的,那就是使用“[[”關鍵字。
“[[”並不是所有的 shell 都支持,它不兼容Bourne Shell。我們可以用 type 命令來查看它的類型。

上面的腳本中的[]內的雙引號就可以不用了,但要注意的是 cat 里的雙引號還是要有的。

FILE="my file"
[[ -f $FILE && -r $FILE ]] && cat "$FILE"

“[[”還有其他高級的功能:

1. 模式匹配

例如,我們需要判斷 Perl 腳本,然后做其他的操作,那么就可以這樣寫:

[[ $FILE = *.pl ]] && cp "$FILE" scripts/
2. 正則表達式

我們可以使用“=~”來匹配正則表達式。我們可以用正則表達式重寫上面的腳本。

$ [[ $FILE =~ \.pl$ ]] &&cp "$FILE" scripts/

我們還可以使用“(())”運算符來做一些簡單的運算。

1. 簡單的數學運算

我們可以使用“((”做一些簡單的數學運算,它可以替代 let 這個內置命令。下面兩行的代碼執行結果是一樣的。

a=(( 2 + 3 ))
let a=2+3
2. 用在循環累加或累減中

這種方式更加常用,

COUNT=1
(( COUNT++ ))
echo $COUNT

3. 用於運算檢查中

我們也可以把“((”用在 test 判斷條件中,我們可以是用“>” 符號,來替代“-gt”。

(( COUNT > 1 )) && echo "Count is greater than 1"

四. 迭代循環

循環語句是任何一門語言都不能缺失的部分。shell 里也是一樣,只是語法不太一樣。如果學過其他的編程語言,就很容易掌握。

1. for 循環

for 循環的語法疾結構為:

for f in * ; do
    statement "$f"
done

這里的 f 就是迭代的元素,* 可以是一個數組或是 list,也可以是命令管道。
還有另外一種寫法:

for f in * 
do
    statement "$f"
done

可以根據自己的喜好選擇一種寫法。
新建一個腳本文件,打印出所有輸入的參數:

#!/bin/bash
echo "You are using $(basename $0)"
for n in $*
do
    echo "Hello $n"
done

exit 0

運行結果如下:

for loop

在循環中,可以使用continuebreak關鍵字,具體用法與其他語言里是一樣的。continue表示在循環體內,跳過當前循環,執行下次的循環;而break表示退出整個循環,后面的循環和代碼不再執行。
看具體看例子。

$ for f in * ; do
    [ -d "$f" ] || continue
         chmod 3777 "$f"
done

如果是目錄,添加權限;如果不是,跳過當前循環,continue 后面代碼不再執行,而是直接執行下次循環。

$ for f in * ; do
    [ -d "$f" ] && break
done

echo "We have found a directory $f"

上面的腳本,在循環中一旦發現目錄,則立即停止循環並退出。

2. while 循環

while 循環可以說是 for 循環的一個變體,只要特定條件為真,while 語句就會執行。具體看例子,

COUNT=10
while (( COUNT >= 0 )) ; do
    echo -e "$COUNT \c"
    (( COUNT-- ))
done ;

 echo

3. until 循環

until循環與while語句的功能正好相反:只要特定條件為假,它就重復。下面是一個與前面的 while 循環具有同等功能的 until 循環。

COUNT=10
until (( COUNT < 0 )) ; do
    echo -e "$COUNT \c"
    (( COUNT-- ))
done ; 

echo

4. 實例練習

現在,我們做一個用戶選擇界面,這樣,根據提示輸入不同的參數來執行不同的功能,這里我們需要用到while循環,和前面講過的case條件選擇。

#!/bin/bash

while true ; do
    clear
    echo "Choose an item: a, b or c"
    echo "a: Backup"
    echo "b: Display Calendar"
    echo "c: Exit"
	
    read -sn1
        case "$REPLY" in
        a) tar -czvf $HOME/backup.tgz ${HOME}/JavaSource;;
        b) cal;;
        c) exit 0;;
        esac
    read -n1 -p "Press andy key to continue"
done

根據提示,如果輸入 a 的話,則把 home 目錄下的 JavaSource 目錄壓縮打包。
輸入b,顯示當前月份。
輸入c,程序退出。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM