Shell编程入门总结(二)

来源:互联网 发布:手机qq皮肤制作软件 编辑:程序博客网 时间:2024/05/17 01:09

本文的主要内容:

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 numberscount=1while [ $count -le 5 ]; do    echo $count    count=$((count + 1))doneecho "Finished."



2.跳出循环
  • break 命令立即终止一个循环, 且程序继续执行循环之后的语句。continue 命令导致程序跳过循环中剩余的语句,且程序继续执行 下一次循环

  • 菜单例子:
#!/bin/bash# while-menu2: a menu driven system information programDELAY=3 # Number of seconds to display resultswhile 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    fidoneecho "Program terminated."



3.until,当遇到一个0退出状态的时候until退出循环
  • 例如(循环输出1-5):
#!/bin/bash# until-count: display a series of numberscount=1until [ $count -gt 5 ]; do    echo $count    count=$((count + 1))doneecho "Finished."



4.使用循环读取文件(其中文件distros.txt中的每一行包含三个字段:distro version release)
  • 重定向文件到循环中
#!/bin/bash# while-read: read lines from a filewhile read distro version release; do    printf "Distro: %s\tVersion: %s\tReleased: %s\n" \        $distro \        $version \        $releasedone < distros.txt

  • 从标准输入管道到循环中
#!/bin/bash# while-read2: read lines from a filesort -k 1,1 -k 2n distros.txt | while read distro version release; do    printf "Distro: %s\tVersion: %s\tReleased: %s\n" \        $distro \        $version \        $releasedone





二、流程控制:case 分支
1.case语句
  • Bash 的多选复合命令称为 case,其语法规则如下:
case word in
    [pattern [| pattern]...) commands ;;]...
esac


  • 使用case的菜单
#!/bin/bash# case-menu: a menu driven system information programclearecho "Please Select:1. Display System Information2. Display Disk Space3. Display Home Space Utilization0. 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/bashread -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 characterread -n 1 -p "Type a character > "echocase $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 filewhile [[ -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    shiftdone



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 commandfor (( i=0; i<5; i=i+1 )); do    echo $idone




四、位置参数

1.访问命令行

  • shell 提供了一个称为位置参数的变量集合,这个集合包含了命令行中所有独立的单词。这些变量按照从0到9给予命名

  • 例如

#!/bin/bash# posit-param: script to view command line parametersecho "\$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 parametersecho "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 argumentscount=1while [[ $# -gt 0 ]]; do    echo "Argument $count = $1"    count=$((count + 1))    shiftdone
当执行$ 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 programPROGNAME=$(basename $0)if [[ -e $1 ]]; then    echo -e "\nFile Type:"    file $1    echo -e "\nFile Status:"    stat $1else    echo "$PROGNAME: usage: $PROGNAME file" >&2    exit 1fi

其中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 pagePROGNAME=$(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 optionsinteractive=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    shiftdone# interactive modeif [[ -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    donefi# output html pageif [[ -n $filename ]]; then    if touch $filename && [[ -f $filename ]]; then        write_html_page > $filename    else        echo "$PROGNAME: Cannot write file '$filename'" >&2        exit 1    fielse    write_html_pagefi





参考:《The Linux Command Line》
1 0
原创粉丝点击