美文网首页
Shell 脚本编程(基础篇)

Shell 脚本编程(基础篇)

作者: rollingstarky | 来源:发表于2018-11-15 02:23 被阅读24次

    基础篇

    一、输出信息

    大部分 Shell 命令都会生成自己的输出信息,在脚本运行时打印到终端屏幕上。但是很多时候,仍需要在输出的信息中添加上自己的内容,以提示用户脚本运行时究竟发生着什么,达到更好的交互效果。

    echo 命令可以用来打印字符串内容。

    $  echo This is a test
    This is a test
    $
    

    PS:默认是不需要将 echo 命令后面的字符串包含在一对引号中的

    echo 可以使用引号作为文本字符串的分隔符。如:

    $ echo 'Hello
    > World'
    Hello
    World
    

    当输出的文本内容中本来就有引号出现时,如(Let's see if this'll work),可以结合单、双引号的使用,或者使用转义符\)前缀

    # 引号作为字符串分隔符而不是文本内容
    $ echo Let's see if this'll work
    Lets see if thisll work
    # 使用 \ 前缀进行转义
    $ echo Let\'s see if this\'ll work
    Let's see if this'll work
    # 使用双引号作为分隔符,单引号作为中间的文本正常输出
    $ echo "Let's see if this'll work"
    Let's see if this'll work
    
    脚本的执行权限

    默认创建的脚本文件没有执行权限,不能直接在命令行中运行。
    如创建包含以下内容的脚本文件 info.sh

    #!/bin/bash
    
    echo The time and date are:
    date
    echo "Let's see who's logged into the system:"
    who
    

    直接执行上述脚本时会提示 permission denied 错误:

    $  ./info.sh
    zsh: permission denied: ./info.sh
    

    需要使用 chomd +x filename 命令为该脚本文件添加执行权限后再运行,效果如下:

    $ chmod +x info.sh
    $ ./info.sh
    The time and date are:
    Wed Oct 31 11:48:07 CST 2018
    Let's see who's logged into the system:
    starky   console  Oct 28 12:42
    starky   ttys000  Oct 29 16:52
    starky   ttys001  Oct 31 11:23
    

    二、变量

    1. 系统环境变量

    在 Shell 脚本中可以直接访问系统中的环境变量,以获取相关的系统信息(如计算机名称,当前登录用户的账户名、 UID 和主目录等)。
    当前定义的所有环境变量可以通过 set 命令获取。

    $ set
    ...
    HISTCMD=2217
    HISTFILE=/Users/starky/.zsh_history
    HISTSIZE=50000
    HOME=/Users/starky
    HOST=skitars-MacBook-Pro.local
    IFS=$' \t\n\C-@'
    ITERM_PROFILE=starky
    ...
    

    当需要在 Shell 脚本中使用具体某个环境变量的值时,可以用环境变量名称加上 $ 前缀表示(如 $HOST)。
    编辑如下 sys_info.sh 文件:

    #!/bin/bash
    
    echo User info for userid: $USER
    echo UID: $UID
    echo HOME: $HOME
    

    运行效果如下:

    $ chmod +x sys_info.sh
    $ ./sys_info.sh
    User info for userid: starky
    UID: 501
    HOME: /Users/starky
    

    PS:由于在 Shell 脚本中,$ 作为变量的前缀符,所以当需要在文本输出中显示 $ 时,应使用转义。

    # $15 被当成了代入到字符串中的“变量”
    $ echo "The cost of the item is $15"
    The cost of the item is
    # 使用 \ 转义后正常打印 $ 字符
    $ echo "The cost of the item is \$15"
    The cost of the item is $15
    
    2. 用户自定义变量

    Shell 脚本允许用户自行定义和使用变量,这样就可以将脚本中用到的数据临时存储在指定的变量中,使用时再通过 $变量名 的形式获取。

    • 变量赋值:var=value (注意 = 号两边不能有空格,即 var = value 是错误的)
    • 变量使用:$var

    PS:Shell 脚本中的变量名区分大小写
    Shell 脚本会自动判断变量值的数据类型
    变量的有效性贯穿脚本的整个生命周期,即脚本执行完毕后变量会自行删除

    编辑如下 variables.sh 文件:

    #!/bin/bash
    
    days=10
    guest="Katie"
    echo "$guest checked in $days days ago"
    
    days=5
    guest="Jessica"
    echo "$guest checked in $days days ago"
    

    运行效果如下:

    $ chmod +x variables.sh
    $ ./variables.sh
    Katie checked in 10 days ago
    Jessica checked in 5 days ago
    $ echo $days
    
    

    可以看到,脚本退出后,脚本中定义的 $days 变量又恢复为未定义的状态。

    三、命令替换

    Shell 脚本最有用处的特性之一,就是它可以提取某个命令的输出信息,并将其赋值给一个变量
    可以通过以下两种方式将命令输出赋值给变量:

    • 反单引号(`)
    • $() 格式

    如:

    # 使用 date 命令获取当前的日期和时间
    $ date
    Thu Nov  1 01:03:02 CST 2018
    # 将 date 命令的输出(即当前日期和时间)赋值给 var1 变量
    $ var1=`date`
    $ echo Today is: $var1
    Today is: Thu Nov  1 01:03:15 CST 2018
    # 将 date 命令的输出赋值给 var2 变量(使用 $() 格式)
    $ var2=$(date)
    $ echo Today is: $var2
    Today is: Thu Nov  1 01:03:36 CST 2018
    

    示例程序
    使用命令替换完成一个脚本(log.sh),该脚本可以创建以当前时间水印为后缀的文本文件,内容为 /usr/bin 目录下的所有文件列表。

    #!/bin/bash
    
    today=$(date +%y%m%d%H%M%S)
    ls -al /usr/bin > log.$today
    echo The file log.$today has been created, you can check it later.
    

    其中 date +%y%m%d%H%M%S 命令可以输出纯数字格式的日期和时间
    运行效果如下:

    $ chmod +x log.sh
    $ ./log.sh
    The file log.181101012159 has been created, you can check it later.
    $ ls log*
    log.181101012159 log.sh
    $ head log.181101012159
    total 103992
    drwxr-xr-x  971 root   wheel     31072 Oct 13 17:44 .
    drwxr-xr-x@   9 root   wheel       288 Sep 21 12:01 ..
    -rwxr-xr-x    4 root   wheel       925 Aug 18 08:45 2to3-
    lrwxr-xr-x    1 root   wheel        74 Oct 13 17:44 2to3-2.7 -> ../../System/Library/Frameworks/Python.framework/Versions/2.7/bin/2to3-2.7
    -rwxr-xr-x    1 root   wheel     55072 Sep 21 12:16 AssetCacheLocatorUtil
    -rwxr-xr-x    1 root   wheel     53472 Sep 21 12:16 AssetCacheManagerUtil
    -rwxr-xr-x    1 root   wheel     48256 Sep 21 12:17 AssetCacheTetheratorUtil
    -rwxr-xr-x    1 root   wheel     18320 Sep 21 12:17 BuildStrings
    -rwxr-xr-x    1 root   wheel     18288 Sep 21 12:17 CpMac
    

    四、重定向输入和输出

    1. 输出重定向

    最基本的重定向,就是通过大于号(>),将某个命令的输出内容保存至一个文件中。

    格式:command > outputfile

    $ date > current_date.txt
    $ ls -l current_date.txt
    -rw-r--r--  1 starky  staff  29 Nov  1 01:29 current_date.txt
    $ cat current_date.txt
    Thu Nov  1 01:29:45 CST 2018
    

    PS:如使用重定向时,指定的文件已存在,则该文件的原始内容会被新内容覆盖
    如果只是想在文件末尾追加内容,则可以使用双大于号(>>)

    $ date >> current_date.txt
    $ cat current_date.txt
    Thu Nov  1 01:29:45 CST 2018
    Thu Nov  1 01:34:03 CST 2018
    
    2. 输入重定向

    输入重定向和输出重定向相反。即从文件中读取内容,并将该内容传递给某个命令。

    格式:command < inputfile

    $ ls -l /Users/starky
    total 49864
    drwx------@  3 starky  staff        96 Oct 13 18:00 Applications
    drwx------+ 23 starky  staff       736 Oct 31 19:14 Desktop
    drwx------+ 21 starky  staff       672 Oct 31 19:15 Documents
    drwx------+ 37 starky  staff      1184 Oct 30 20:18 Downloads
    drwx------+ 72 starky  staff      2304 Oct 27 01:38 Library
    drwx------+  6 starky  staff       192 Oct 29 10:34 Movies
    drwx------+  3 starky  staff        96 Sep 27 13:13 Music
    ...
    $ cat directory.txt
    /Users/starky
    $ ls -l < directory.txt
    total 49864
    drwx------@  3 starky  staff        96 Oct 13 18:00 Applications
    drwx------+ 23 starky  staff       736 Oct 31 19:14 Desktop
    drwx------+ 21 starky  staff       672 Oct 31 19:15 Documents
    drwx------+ 37 starky  staff      1184 Oct 30 20:18 Downloads
    drwx------+ 72 starky  staff      2304 Oct 27 01:38 Library
    drwx------+  6 starky  staff       192 Oct 29 10:34 Movies
    drwx------+  3 starky  staff        96 Sep 27 13:13 Music
    ...
    
    3. 管道

    有些时候,需要将某个命令的输出内容作为另一个命令的输入。如:

    $ ls -al > tmp_file
    $ grep vim < tmp_file
    drwxr-xr-x    3 starky  staff        96 Oct 20 15:45 .vim
    -rw-------    1 starky  staff     23799 Nov  1 01:40 .viminfo
    -rw-r--r--    1 starky  staff      3935 Oct 21 01:31 .vimrc
    -rw-r--r--    1 starky  staff  24849808 Oct 25 21:02 vim.tar.gz
    

    上面的命令先将当前目录下的文件列表(ls -al)保存在 tmp_file 中,再使用 grep 命令读取 tmp_file 的内容,筛选文件名中包含 vim 的文件。

    其实可以通过管道|)的使用,将前面命令的输出,定向给后面的命令作为输入。

    格式:command1 | command2

    ls -al | grep vim
    drwxr-xr-x    3 starky  staff        96 Oct 20 15:45 .vim
    -rw-------    1 starky  staff     23799 Nov  1 01:40 .viminfo
    -rw-r--r--    1 starky  staff      3935 Oct 21 01:31 .vimrc
    -rw-r--r--    1 starky  staff  24849808 Oct 25 21:02 vim.tar.gz
    

    五、数学运算

    操作数学运算对于任何编程语言来说,都是一个很重要的特性。但是 Shell 脚本并不能直接完成算术运算的操作,只能通过以下两种方式来实现。

    1. expr 命令

    Shell 提供了一个特殊的命令(expr)用来处理数学算式,如:

    $ expr 1 + 2
    3
    

    PS:注意算式中 + 号两边的空格
    expr 命令支持的算术操作符如下:

    操作符 含义
    ARG1 || ARG2 如果两个参数值都不为 null 或 0,返回 ARG1,否则返回 ARG2
    ARG1 && ARG2 如果两个参数值都不为 null 或 0,返回 ARG1,否则返回 0
    ARG1 < ARG2 如果 ARG1 小于 ARG2,返回 1,否则返回 0
    ARG1 > ARG2 如果 ARG1 大于 ARG2,返回 1,否则返回 0
    ARG1 = ARG2 如果 ARG1 等于 ARG2,返回 1,否则返回 0
    ARG1 >= ARG2 如果 ARG1 大于或等于 ARG2,返回 1,否则返回 0
    ARG1 <= ARG2 如果 ARG1 小于或等于 ARG2,返回 1,否则返回 0
    ARG1 != ARG2 如果 ARG1 不等于 ARG2,返回 1,否则返回 0
    ARG1 + ARG2 返回 ARG1 与 ARG2 的数字加和
    ARG1 - ARG2 求 ARG1 减去 ARG2 的数字差
    ARG1 * ARG2 返回 ARG1 与 ARG2 的数字乘积
    ARG1 / ARG2 求 ARG1 除以 ARG2 的数字商(结果为整数)
    ARG1 % ARG2 对 ARG1 和 ARG2 进行求余操作

    数学运算示例(divide.sh):

    #!/bin/bash
    
    var1=10
    var2=20
    var3=`expr $var2 / $var1`
    
    echo $var2 divided by $var1 equals $var3
    

    运行效果:

    $ chmod +x divide.sh
    $ ./divide.sh
    20 divided by 10 equals 2
    
    2. 使用中括号

    Bash Shell 中的 expr 命令主要是为了保持和 Bourne Shell 的兼容性,它其实还提供了一种更简单的方式用来处理数学运算。即使用这样的形式:
    $[ operation ]

    如下面的脚本(compute.sh

    #!/bin/bash
    
    var1=100
    var2=50
    var3=45
    
    result=$[$var1 * ($var2 - $var3)]
    echo The final result is $result
    

    运行效果:

    $ chmod +x compute.sh
    $ ./compute.sh
    The final result is 500
    

    但是在进行除法运算时,上面的方式只支持整数。如两个数相除结果为小数,则该结果会舍去小数部分只保留整数。

    $ var1=10
    $ var2=3
    $ result=$[$var1 / $var2]
    $ echo The result is $result
    The result is 3
    
    3. 浮点数运算

    有很多种方案可以克服 bash 的整数限制,最常用的一种就是使用系统内置的 bash calculator,即 bc 程序。
    bash calculator 其实是一种支持浮点数运算的编程语言,可以识别以下几种类型的数据:

    • 数字(整数和浮点数
    • 变量(简单变量和数组
    • 注释(单行注释 # 和多行注释 /* */
    • 表达式
    • 编程语句(如 if-then 语句)
    • 函数
    $ bc
    bc 1.06
    Copyright 1991-1994, 1997, 1998, 2000 Free Software Foundation, Inc.
    This is free software with ABSOLUTELY NO WARRANTY.
    For details type `warranty'.
    12 * 5.4
    64.8
    3.156 * (3 + 5)
    25.248
    quit
    

    计算结果精确到的小数位数是通过一个内建的 scale 变量定义的,默认的 scale 数值为 0(即默认舍去商的小数位数):

    $ bc -q
    3.44 / 5
    0
    scale=4
    3.44 / 5
    .6880
    quit
    

    bc 程序也支持自定义变量:

    $ bc -q
    var1=10
    var1 * 4
    40
    var2=var1 / 5
    print var2
    2
    quit
    
    4. 在脚本中使用 bc

    可以通过管道将数学表达式传递给 bc 程序,再将计算得出的结果通过赋值语句赋值给某个变量:
    variable=$(echo "options; expression" | bc)

    其中 variable=$(...) 用于提取命令的输出并赋值给一个变量(参考第三章命令替换

    如下面的脚本(bc1.sh):

    #!/bin/bash
    
    var1=100
    var2=45
    var3=$(echo "scale=4; $var1 / $var2" | bc)
    
    echo The answer for this is $var3
    

    运行效果:

    $ chmod +x bc1.sh
    $ ./bc1.sh
    The answer for this is 2.2222
    
    更复杂的形式

    在脚本中使用 bc 还可以通过如下的形式:

    variable=$(bc << EOF
    options
    statements
    expressions
    EOF
    )
    

    示例程序如下(bc2.sh):

    #!/bin/bash
    
    var1=10.46
    var2=43.67
    var3=33.2
    var4=71
    
    var5=$(bc << EOF
    scale = 4
    a1=$var1 * $var2
    b1=$var3 * $var4
    a1 + b1
    EOF
    )
    
    echo The final answer for this mess is $var5
    

    运行结果:

    $ chmod +x bc2.sh
    $ ./bc2.sh
    The final answer for this mess is 2813.9882
    

    参考资料

    Linux Command Line and Shell Scripting Bible 3rd Edition

    相关文章

      网友评论

          本文标题:Shell 脚本编程(基础篇)

          本文链接:https://www.haomeiwen.com/subject/mcyrtqtx.html