一、前言

本文将简单探索shell脚本编程,介绍shell的基本语法。

二、shell简介

shell是一个命令解释器,可以用来启动、停止、编写程序;是用户和UNIX/Linux操作系统内核程序间的一个接口。

而shell编程则是将linux命令与shell的各种流程控制和条件判断来组合成命令与变量,形成可以进行自动处理的脚本程序。

三、前期准备

创建脚本

shell脚本是一个文本文件,可用文本编辑器如vi、vim编辑保存。创建shell脚本只需按照创建文本文件的方式创建。如

vi c1.sh
vim c2.sh
> c3.sh

shell脚本一般以.sh为后缀,但没有后缀依旧可以执行。

创建的shell脚本,一定要在开头第一行加上如下语句:

#!/bin/bash

这一行将指明该脚本执行所需要的命令解释器。

执行脚本

shell脚本的执行方法有

sh <scriptname>
bash <scriptname>

或者使用chmod命令修改脚本为可执行,再直接使用 ./<scriptname> 运行。

四、基本语法

变量

shell中的变量分为环境变量、用户定义变量、内部变量。

其中环境变量是操作系统的一部分,但可以利用shell脚本进行修改;用户变量即在脚本中声明的变量;而内部变量则用来指示脚本运行中出现的一些变量。

声明

只有用户变量可以声明。和其他语言一样,使用等号进行声明。但要注意的是,shell脚本是弱类型的,因此变量名前不需要加上类型名。

var=hello_world

注意shell对空格敏感,声明时等号两边不能有空格

另外可以在变量名前添加 readonly关键字设为只读

readonly constVar

shell中声明数组同样直接写出数组名称

arr[0]=1
arr[1]=5
arr[10]=20

未赋值的部分默认为NULL。

注意 ubuntu 默认使用 dash 而非 bash shell。dash 并不支持数组。要使用数组可以用bash运行脚本,即运行命令 bash <scriptname>

赋值

shell变量有类似左值右值的区别。在用其他变量进行赋值时,需要对变量使用${ } 进行取值。

var1=hi
var2=${var1}

引号

shell脚本是为了自动化处理命令而设计的。因此语法中有很大一部分关注于字符串和命令的相关操作。在变量上体现在,shell中所有变量默认以字符串形式存在。

并且,为了满足命令处理的需要,shell设计出了引号变量值。

shell中的引号包括单引号、双引号和倒引号。

单引号中的字符均作为普通字符出现。(可以包括空格)

var1=hello_world
# var2=hello world # 不合法
var3='hello world'

双引号中的字符大部分作为普通字符对待。除了$\’和双引号,这些变量依旧用于对字符串内容进行变量替换。

var4=hello
var5=friend
var6="$var4, my $var5!"
# var6 means "hello, my friend!".

倒引号将引号内的内容当做命令,会先执行倒引号内的内容,再用执行后的输出替换倒引号的内容。

倒引号可以和双引号组合使用,在双引号内使用倒引号 $(command) 与 `command` 功能相同

var7="now pwd: `pwd`"
# var6 means "now pwd: /home/username" (depend on the position where you execute the script)

基础操作

字符串操作

shell脚本自身具有一些字符串操作功能。

  • 字符串长度:
    ${#str}
    
  • 字符串截取:
    ${str:position}
    ${str:start:length}
    
  • 从字符串开头删除:
    ${str#substr}
    ${str##substr}
    
  • 从字符串末尾删除:
    ${str%substr}
    ${str%%substr}
    
  • 字符串替换:
    ${str/substr/replace}
    

字符串操作中出现匹配的部分都支持正则表达式。其中一个字符的为懒惰匹配,两个字符的为贪婪匹配。

使用 expr 等命令,可以更好地进行处理。

数字运算

shell默认变量为字符串,因此若要进行数字运算,需要特殊指明。

具体地,有两种方法。一种是利用 $(()) 运算符。指明括号内的表达式进行的不是字符串操作而是数字运算。

num1=10
num2=20
num3=$(($num1+$num2)) # 30

另一种方法是调用 linux 的 expr 命令。

num4=3
num5=$num4
num6=`expr $num4 \* $num5` # 9

注意乘号在shell脚本中有特殊含义,因此需要加入反斜杠转义。另外 expr 命令中变量和运算符各自作为参数,因此中间需要用空格隔开.

流程控制

条件表达式

shell 利用 test condition[condition] 进行条件测试。其中“condition” 表示一个条件表达式。包括字符比较、数值比较、文件操作、逻辑操作。

这一部分原理较为简单,但shell中判断符号与其他语言有较大差别,内容较多,就不一一列举了。

注意使用条件表达式时,表达式与方括号间要有空格隔开

分支控制

分支控制语句如下

if 分支:

if [ condition ]
then
    commands
elif
    commands
else
    commands
fi

注意末尾的 fi

case分支(类似switch):

case variable in
var1)
    commands
    ;;
var2 | var3)
    commands
    ;;
*)
    commands
esac

注意两个连续的分号,类似于 break。进入同样分支的变量值要用 “|” 隔开。末尾要添加 esac。

循环控制

循环控制语句如下

while 循环:

while [ condition ]
do
    commands
done

until 循环:

until [ condition ]
do
    commands
done

until 循环与条件取反的 while 循环等价

for 循环如下:

for arg in args
do
    commands
done

其中 args 是一组列出的变量名或字符串

函数

函数的定义

function func_name{
    commands
}

func_name(){
    commands
}

两种方式均可定义函数。

参数取值

shell中的函数只有可变参数。另外,整个shell脚本本身也可以看做一个函数,它同样也只有可变参数。都具有相同的取参数的方式。

shell中通过 $0 $1 $2 ... $9等参数来获取函数对应位置的参数。其中 $0 最开始为函数名/脚本名。当参数多于 10 个时,要获取十个之外的参数,可以使用 shift 命令,使得除了函数名/脚本名 $0 之外的所有参数整体左移一位。

# sh <scriptname>.sh 123 456
echo $1 # 123
shift
echo $1 # 456

echo 为输出命令

函数返回值

函数通过 return [n] 语句来返回变量值 n。如果没有设置返回值,那么会默认返回函数最后一条命令执行后的返回值。

在函数调用之后,可以使用 $? 获取函数的返回值。

注意 return 的值必须只能是 0~255间的整数。要返回字符串或者更大的数字,可以直接使用 echo 输出内容,再通过倒引号获取输出的内容。

函数实例

编写一个递归计算阶乘的函数

frac()
{
    if [ $(($1)) -ge 1 ]
    then
        echo $((`frac $(($1-1))`*$1))
    else
        echo 1
    fi
}

echo `frac 5` # 120