歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux編程 >> SHELL編程

Shell編程入門總結(二)

本文的主要內容:1、流程控制:while/until 循環

2、流程控制:case 分支3、流程控制:for 循環

4、位置參數一、流程控制:while/until 循環

1.while,當遇到一個非零退出狀態的時候while 退出循環while語法:while commands; do commands; done

例如(循環輸出1-5):

#!/bin/bash
# while-count: display a series of numbers
count=1
while [ $count -le 5 ]; do
    echo $count
    count=$((count + 1))
done
echo "Finished."
2.跳出循環

break 命令立即終止一個循環, 且程序繼續執行循環之後的語句。continue 命令導致程序跳過循環中剩余的語句,且程序繼續執行 下一次循環

菜單例子:

#!/bin/bash
# while-menu2: a menu driven system information program
DELAY=3 # Number of seconds to display results
while true; do
    clear
    cat <<- _EOF_
        Please Select:
        1. Display System Information
        2. Display Disk Space
        3. Display Home Space Utilization
        0. Quit
    _EOF_
    read -p "Enter selection [0-3] > "
    if [[ $REPLY =~ ^[0-3]$ ]]; then
        if [[ $REPLY == 1 ]]; then
            echo "Hostname: $HOSTNAME"
            uptime
            sleep $DELAY
            continue
        fi
        if [[ $REPLY == 2 ]]; then
            df -h
            sleep $DELAY
            continue
        fi
        if [[ $REPLY == 3 ]]; then
            if [[ $(id -u) -eq 0 ]]; then
                echo "Home Space Utilization (All Users)"
                du -sh /home/*
            else
                echo "Home Space Utilization ($USER)"
                du -sh $HOME
            fi
            sleep $DELAY
            continue
        fi
        if [[ $REPLY == 0 ]]; then
            break
        fi
    else
        echo "Invalid entry."
        sleep $DELAY
    fi
done
echo "Program terminated."
3.until,當遇到一個0退出狀態的時候until退出循環

例如(循環輸出1-5):

#!/bin/bash
# until-count: display a series of numbers
count=1
until [ $count -gt 5 ]; do
    echo $count
    count=$((count + 1))
done
echo "Finished."
4.使用循環讀取文件(其中文件distros.txt中的每一行包含三個字段:distro version release)

重定向文件到循環中

#!/bin/bash
# while-read: read lines from a file
while read distro version release; do
    printf "Distro: %s\tVersion: %s\tReleased: %s\n" \
        $distro \
        $version \
        $release
done < distros.txt
從標准輸入管道到循環中

#!/bin/bash
# while-read2: read lines from a file
sort -k 1,1 -k 2n distros.txt | while read distro version release; do
    printf "Distro: %s\tVersion: %s\tReleased: %s\n" \
        $distro \
        $version \
        $release
done
二、流程控制:case 分支

1.case語句Bash 的多選復合命令稱為 case,其語法規則如下:

case word in

[pattern [| pattern]...) commands ;;]...

esac

使用case的菜單

#!/bin/bash
# case-menu: a menu driven system information program
clear
echo "
Please Select:
1. Display System Information
2. Display Disk Space
3. Display Home Space Utilization
0. Quit
"
read -p "Enter selection [0-3] > "
case $REPLY in
    0)  echo "Program terminated."
        exit
        ;;
    1)  echo "Hostname: $HOSTNAME"
        uptime
        ;;
    2)  df -h
        ;;
    3)  if [[ $(id -u) -eq 0 ]]; then
            echo "Home Space Utilization (All Users)"
            du -sh /home/*
        else
            echo "Home Space Utilization ($USER)"
            du -sh $HOME
        fi
        ;;
    *)  echo "Invalid entry" >&2
        exit 1
        ;;
esac
2.模式

case 語句使用的模式和路徑展開中使用的相同。模式以一個 “)” 為終止符。

常用的有效模式:

a):若單詞為 “a”,則匹配

[[:alpha:]]):若單詞是一個字母字符,則匹配

???):若單詞只有3個字符,則匹配

*.txt):若單詞以 “.txt” 字符結尾,則匹配

*):匹配任意單詞。可將此模式做為 case 命令的最後一個模式,以捕捉到任何可能的無效值。

模式例子:

#!/bin/bash
read -p "enter word > "
case $REPLY in
    [[:alpha:]])        echo "is a single alphabetic character." ;;
    [ABC][0-9])         echo "is A, B, or C followed by a digit." ;;
    ???)                echo "is three characters long." ;;
    *.txt)              echo "is a word ending in '.txt'" ;;
    *)                  echo "is something else." ;;
esac
還可以使用豎線字符作為分隔符,把多個模式結合起來形成一個 “或”條件模式,如" q|Q) "可匹配大寫或小寫的'q'

3.匹配多個測試條件以執行多個動作添加“;;&” 的語法允許 case 語句繼續執行下一條測試,而不是簡單地終止運行

例子:

#!/bin/bash
# case4-2: test a character
read -n 1 -p "Type a character > "
echo
case $REPLY in
    [[:upper:]])    echo "'$REPLY' is upper case." ;;&
    [[:lower:]])    echo "'$REPLY' is lower case." ;;&
    [[:alpha:]])    echo "'$REPLY' is alphabetic." ;;&
    [[:digit:]])    echo "'$REPLY' is a digit." ;;&
    [[:graph:]])    echo "'$REPLY' is a visible character." ;;&
    [[:punct:]])    echo "'$REPLY' is a punctuation symbol." ;;&
    [[:space:]])    echo "'$REPLY' is a whitespace character." ;;&
    [[:xdigit:]])   echo "'$REPLY' is a hexadecimal digit." ;;&
esac
當輸入'a'時能匹配[[:lower:]]、[[:alpha:]]、[[:graph:]]和[[:xdigit:]]並顯示相應輸出

三、流程控制:for 循環1.for: 傳統 shell 格式

for variable [in words]; do

commands

done

例如:

$ for i in A B C D; do echo $i; done

A

B

C

D

或者通過花括號展開:

$ for i in {A..D}; do echo $i; done

A

B

C

D

或者路徑名展開:

$ for i in distros*.txt; do echo $i; done

distros-by-date.txt

distros-dates.txt

distros-key-names.txt

distros-names.txt

distros.txt

或者命令替換:

#!/bin/bash
# longest-word : find longest string in a file
while [[ -n $1 ]]; do
    if [[ -r $1 ]]; then
        max_word=
        max_len=0
        for i in $(strings $1); do
            len=$(echo $i | wc -c)
            if (( len > max_len )); then
                max_len=$len
                max_word=$i
            fi
        done
        echo "$1: '$max_word' ($max_len characters)"
    fi
    shift
done
2.for: C 語言格式

for (( expression1; expression2; expression3 )); do

commands

done

在行為方面,這相當於以下構造形式:

(( expression1 ))

while (( expression2 )); do

commands

(( expression3 ))

done

例如輸出0-4:

#!/bin/bash
# simple_counter : demo of C style for command
for (( i=0; i<5; i=i+1 )); do
    echo $i
done
四、位置參數

1.訪問命令行shell 提供了一個稱為位置參數的變量集合,這個集合包含了命令行中所有獨立的單詞。這些變量按照從0到9給予命名

例如

#!/bin/bash
# posit-param: script to view command line parameters
echo "
\$0 = $0
\$1 = $1
\$2 = $2
"
當執行$ posit-param時運行結果:

$0 = /home/me/bin/posit-param

$1 =

$2 =

其中位置參數 $0 為執行程序的路徑名

訪問大於9的參數時,用花括號把該數字括起來即可。 例如 ${10}, ${666} ...

2.確定參數個數shell 提供了一個名為 $#的變量,可以得到命令行參數個數

例如:

#!/bin/bash
# posit-param: script to view command line parameters
echo "
Number of arguments: $#
\$0 = $0
\$1 = $1
\$2 = $2
"
當執行$ posit-param a b 時運行結果為Number of arguments: 2

$0 = /home/me/bin/posit-param

$1 = a

$2 = b

3.shift - 訪問多個參數的利器執行一次 shift 命令, 就會使所有的位置參數“向下移動一個位置”

例如:

#!/bin/bash
# posit-param2: script to display all arguments
count=1
while [[ $# -gt 0 ]]; do
    echo "Argument $count = $1"
    count=$((count + 1))
    shift
done
當執行$ posit-param2 a b c時運行結果為:Argument 1 = a

Argument 2 = b

Argument 3 = c

每次 shift 命令執行的時候,變量 $2 的值會移動到變量 $1 中,變量 $3 的值會移動到變量 $2 中,依次類推。 變量 $# 的值也會相應的減1。

4.簡單應用顯示一個具體文件的文件類型(由 file 命令確定)和文件狀態(來自 stat 命令)

#!/bin/bash
# file_info: simple file information program
PROGNAME=$(basename $0)
if [[ -e $1 ]]; then
    echo -e "\nFile Type:"
    file $1
    echo -e "\nFile Status:"
    stat $1
else
    echo "$PROGNAME: usage: $PROGNAME file" >&2
    exit 1
fi
其中basename 命令清除 一個路徑名的開頭部分,只留下一個文件的基本名稱

5.Shell 函數中使用位置參數例如:

file_info () {
  # file_info: function to display file information
  if [[ -e $1 ]]; then
      echo -e "\nFile Type:"
      file $1
      echo -e "\nFile Status:"
      stat $1
  else
      echo "$FUNCNAME: usage: $FUNCNAME file" >&2
      return 1
  fi
}
6.處理集體位置參數

shell 提供了兩種特殊的參數:

$*:展開成一個從1開始的位置參數列表。當它被用雙引號引起來的時候,展開成一個由雙引號引起來 的字符串,包含了所有的位置參數,每個位置參數由 shell 變量 IFS 的第一個字符(默認為一個空格)分隔開。

$@:展開成一個從1開始的位置參數列表。當它被用雙引號引起來的時候, 它把每一個位置參數展開成一個由雙引號引起來的分開的字符串。

例如:

#!/bin/bash
# posit-params3 : script to demonstrate $* and $@
print_params () {
    echo "\$1 = $1"
    echo "\$2 = $2"
    echo "\$3 = $3"
    echo "\$4 = $4"
}
pass_params () {
    echo -e "\n" '$* :';      print_params   $*
    echo -e "\n" '"$*" :';    print_params   "$*"
    echo -e "\n" '$@ :';      print_params   $@
    echo -e "\n" '"$@" :';    print_params   "$@"
}
pass_params "word" "words with spaces"
運行結果為:

$ posit-param3

$* :

$1 = word

$2 = words

$3 = with

$4 = spaces

"$*" :

$1 = word words with spaces

$2 =

$3 =

$4 =

$@ :

$1 = word

$2 = words

$3 = with

$4 = spaces

"$@" :

$1 = word

$2 = words with spaces

$3 =

$4 =

其中通過參數,$* 和 $@ 兩個都產生了一個有四個詞的結果:

"$*" 產生了一個串:"word words with spaces"

"$@" 產生了兩個串:"word" "words with spaces"

目前看來“$@” 在大多數情況下是最有用的方法,因為它保留了每一個位置參數的完整性。

7.實例:輸出一個包含系統信息的網頁

#!/bin/bash
# sys_info_page: program to output a system information page
PROGNAME=$(basename $0)
TITLE="System Information Report For $HOSTNAME"
CURRENT_TIME=$(date +"%x %r %Z")
TIMESTAMP="Generated $CURRENT_TIME, by $USER"
report_uptime () {
    cat <<- _EOF_
        <H2>System Uptime</H2>
        <PRE>$(uptime)</PRE>
    _EOF_
    return
}
report_disk_space () {
    cat <<- _EOF_
        <H2>Disk Space Utilization</H2>
        <PRE>$(df -h)</PRE>
    _EOF_
    return
}
report_home_space () {
    if [[ $(id -u) -eq 0 ]]; then
        cat <<- _EOF_
            <H2>Home Space Utilization (All Users)</H2>
            <PRE>$(du -sh /home/*)</PRE>
        _EOF_
    else
        cat <<- _EOF_
            <H2>Home Space Utilization ($USER)</H2>
            <PRE>$(du -sh $HOME)</PRE>
        _EOF_
    fi
    return
}
usage () {
    echo "$PROGNAME: usage: $PROGNAME [-f file | -i]"
    return
}
write_html_page () {
    cat <<- _EOF_
        <HTML>
            <HEAD>
                <TITLE>$TITLE</TITLE>
            </HEAD>
            <BODY>
                <H1>$TITLE</H1>
                <P>$TIMESTAMP</P>
                $(report_uptime)
                $(report_disk_space)
                $(report_home_space)
            </BODY>
        </HTML>
    _EOF_
    return
}
# process command line options
interactive=
filename=
while [[ -n $1 ]]; do
    case $1 in
        -f | --file)          shift
                              filename=$1
                              ;;
        -i | --interactive)   interactive=1
                              ;;
        -h | --help)          usage
                              exit
                              ;;
        *)                    usage >&2
                              exit 1
                              ;;
    esac
    shift
done
# interactive mode
if [[ -n $interactive ]]; then
    while true; do
        read -p "Enter name of output file: " filename
        if [[ -e $filename ]]; then
            read -p "'$filename' exists. Overwrite? [y/n/q] > "
            case $REPLY in
                Y|y)    break
                        ;;
                Q|q)    echo "Program terminated."
                        exit
                        ;;
                *)      continue
                        ;;
            esac
        fi
    done
fi
# output html page
if [[ -n $filename ]]; then
    if touch $filename && [[ -f $filename ]]; then
        write_html_page > $filename
    else
        echo "$PROGNAME: Cannot write file '$filename'" >&2
        exit 1
    fi
else
    write_html_page
fi

參考:《The Linux Command Line》

Copyright © Linux教程網 All Rights Reserved