Shell脚本编程

一、什么是Shell?

Shell 是一个用 C 语言编写的程序,它是用户使用 Linux 的桥梁。Shell 既是一种命令语言,又是一种程序设计语言。

Shell 是指一种应用程序,这个应用程序提供了一个界面,用户通过这个界面访问操作系统内核的服务。

Ken Thompson 的 sh 是第一种 Unix Shell,Windows Explorer 是一个典型的图形界面 Shell。

二、Linux的Shell种类众多,常用的有:

  • Bourne Shell(/usr/bin/sh或/bin/sh)
  • Bourne Again Shell(/bin/bash)
  • C Shell(/usr/bin/csh)
  • K Shell(/usr/bin/ksh)
  • Shell for Root(/sbin/sh)

其中Bash 在日常工作中被广泛使用。同时,Bash 也是大多数Linux 系统默认的 Shell。

三、Shell编程步骤

1. 创建一个文件,扩展名为.sh注意:扩展名并不影响脚本执行,只是为了见名知意。

2. 修改文件的权限为可执行文件

3. 执行 (eq:./test.sh):./表示当前目录下


四、Shell语法结构

1. #!是一个约定的标记,用来指定执行脚本的shell

2. #注释

3. 命令和控制结构

实例:

#!/bin/bash
echo "Hello World !"

运行结果:


注意:一定要携程./test.sh,而不是test.sh,运行其他二进制的程序也是一样,直接写test.sh,linux系统会去PATH里寻找有没有叫做test.sh的,而只有/bin,/sbin,/usr/bin,/usr/sbin等在PATH里,你的当前目录通常不在PATH里,所以直接写成test.sh是找不到命令的,要用./test.sh告诉系统,就在当前目录下寻找。

五、Shell常用语法:

a)变量:

1.定义变量:
your_name=”xiaoming”

除了显式地直接赋值,还可以使用语句给变量赋值,如

for file in `ls /etc`或for file in $(ls /etc)

注:for循环的使用方法在后面会详细说明

注意:

①变量和等号之间不能有空格。

②习惯用大写字母来命名变量

③命令的执行结果赋值给变量时,使用反单引号如,TIME=`date`此命令可以获得当前系统时间

变量的命名规则:

1. 命名只能使用英文字母,数字和下划线,首个字符不能以数字开头。

2. 中间不能有空格,可以使用下划线(_)

3. 不能使用标点符号。

4. 不能使用bash里的关键字(可使用help命令查看保留关键字)

2.变量的使用:

使用一个定义变量,只要在变量名前加美元符号($),如

your_name="qinjx"
echo $your_name
echo ${your_name}

注:变量名外面的大括号是可以选择的,加不加都行,加大括号的目的只是想让解析器区分变量的边界

3.只读变量

使用readonly,此命令可以将变量定义为只读变量,只读变量的值是不能改变的,如:

#!/bin/bash
name="hello world !"
readonly name
name="hello kitty !"

运行结果:


4.删除变量

使用unset,变量删除后不能再次被使用,unset不能删除只读变量,如

#!/bin/sh
name1="HelloWorld"
name2="HelloKitty"
readonly name2
unset name1
unset name2
echo $name1
echo $name2

运行结果:


注:删除的变量,再次输出为空

5.变量类型:

1)临时变量:也叫做局部变量,在脚本或命令中定义,仅在当前shell实例中有效,其他shell启动的程序不能访问局部变量

2)永久变量:也叫做环境变量,所有的程序,包括shell启动的程序,都能访问环境变量,有些程序需要环境变量来保证其正常运行。必要的时候shell脚本也是可以定义环境变量。

3)shell变量:shell变量是由shell程序设置的特殊变量。shell变量的中的一部分是环境变量,有一部分是局部变量,这些变量保证了shell的正常运行

b)字符串:

字符串是shell编程中最常用最有用的数据类型。字符串可以使用单引号,也可以用双引号,也可以不用引号。

1.单引号字符串的限制:

单引号里的任何字符都会原样输出,单引号字符串中的变量是无效的;

单引号字串中不能出现单引号(对单引号使用转义符后也不行)。

如:

name=HelloWorld
name2=’HelloWorld${name}’
name3=’Hello’World’’
echo $name
echo $name2
echo $name3

运行结果:


2.双引号优点:

双引号里可以有变量

双引号里可以出现转义字符

如:

name=HelloWorld
name2=”HelloWorld${name}”
name3=”Hello\”World\””
echo $name
echo $name2
echo $name3

运行结果:


3.拼接字符串:

两种方式 :

  • 无缝拼接
  • 双引号可以直接使用变量
your_name="小明"
#无缝拼接
greeting="hello, "$your_name" !"
#双引号可以直接使用变量
greeting_1="hello, ${your_name} !"
echo $greeting $greeting_1

运行结果:


4.获取字符串长度

语法1:${#name}

语法2:expr length ${name}

string="abcd"
echo ${#string} #输出4
length=`expr length "${name}"`
Echo $length#输出4
5.提取子字符串

语法:

${name:startIndex:length}#索引开始,截取length长度

${name:startIndex}#开始索引到结尾

${name:0-index:lenght}#索引从后开始,截取length长度

${name:0-index}#索引从后开始,截取到结尾

实例:

string="runoob is a great site"
echo ${string:1:4} # 输出 unoo
echo ${string:1}#输出unoob is a great site
echo ${string:0-4:3}#输出sit
echo ${string:0-4}#输出site

注:还可以使用#、##、%、%%进行删除后保留子串

①#、##:从左边开始删除。

#表示从左边删除到第一个指定字符;

##表示从左边删除到最后一个指定的字符

②%、%%:从右边开始删除

%:表示从右边删除到第一个指定字符

%%:表示从右边删除到第一个指定字符

注:删除包括了指定的字符本身

如:

#使用#、##、%、%%进行删除后保留子串

string="http:/www.baidu.com/a.html"

echo "使用#得到字符串:${string#*/}"

echo "使用##得到的字符串:${string##*/}"

echo "使用%得到字符串:${string%/*}"

echo "使用%%的到字符串:${string%%/*}"

运行结果:

Shell脚本编程_第1张图片

6. 查找子字符串

使用命令expr

string="runoob is a great company"
echo `expr index "$string" is`  # 输出 8

c)Shell数组

1.使用规则

1. Bash仅仅支持一维数组,并且没有限定数组的大小。

2. 数组元素的下标从0开始。

3. 获取数组中的元素要利用下标。

4. 下标使用不当,会报错

2.定义数组:

语法1:arrayName=(值1 值2 ……值n)

语法2:arrayName=(

值1

值2

……

值n

)

注:元素间使用空格分隔开或者换行符

也可以单独定义数组的各个分量:

array_name[0]=value0
array_name[1]=value1
array_name[n]=valuen

注:可以不使用连续的下标,而且下标的范围没有限制

3.读取数组

格式:${数组名[下标]}

如:

#!/bin/bash

#数组的使用规则

#数组第一种赋值方法

my_array1=(A B "C" 34)

#数组第二种赋值方法

my_array2=(

A

B

"C"

34

)

#数组第三种赋值方法
my_array3[0]=A
my_array3[1]=B
my_array3[2]="C"
my_array3[3]=34
#获取单个元素
echo "获取第一个元素:"${my_array1[0]}  "获取第三个元素"${my_array1[2]}

运行结果:


4.获取数组的所有元素

格式:${name[*]}或${name[@]}

#!/bin/bash

#数组的使用规则

#数组第一种赋值方法

my_array1=(A B "C" 34)

#数组第二种赋值方法

my_array2=(

A

B

"C"

34

)

#数组第三种赋值方法
my_array3[0]=A
my_array3[1]=B
my_array3[2]="C"
my_array3[3]=34
#获取所有元素,使用*或@
echo "使用@方式获取my_array:"${my_array1[@]}
echo "使用*方式获取my_array:"${my_array1[*]}
echo "my_array2:"${my_array2[*]}
echo "my_array3:"${my_array3[*]}

运行结果:


5.获取数组长度

格式:${#name[*]}或${#name[@]}

如:

#!/bin/bash

#数组的使用规则

#数组第一种赋值方法

my_array1=(A B "C" 34)

#数组第二种赋值方法

my_array2=(

A

B

"C"

34

)

#数组第三种赋值方法
my_array3[0]=A
my_array3[1]=B
my_array3[2]="C"
my_array3[3]=34
#获取数组长度
echo "my_array1长度:${#my_array1[*]}"
echo "my_array2长度:${#my_array2[*]}"
echo "my_array3长度:${#my_array3[@]}"

运行结果:


d)位置变量:

Shell解释用户执行的命令时,会将命令行的一串字符进行解析划分成部分。每个部分都有一个固定的变量名$n,不能自定义。

$n:   n从0开始,依次往后+1。从$1开始 叫位置参数变量。大于等于10时,必须使用{}将数字括起来

Ex:./test1.sh   file1   file2    file3  …..   file10

$0    的值为:ls  -la

$1    的值为file1

…….

${10} 的值为file10

如:

#位置变量
echo "第一个参数:$0"
echo "第二个参数:$1"
echo "第三个参数:$2"
echo "第十一个参数:$10"
echo "第十一个参数有括号:${10}"

运行结果:

Shell脚本编程_第2张图片

还有其他参数:

变量名称  意义
$*  获取所有参数,合并成一个字符串
$#  参数个数
$$  当前进程的ID
$!  后台运行的最后一个进程的ID
$?  显示最后一个命令的退出状态。0表示没有错误

e)Shell命令:

1.read命令:

从键盘读取数据,赋值给变量

如:

read username
echo "你输入的用户名为:"$username

运行结果:


注1:当输入的词组个数大于需要的参数个数,则多出的词组将被作为整体为最后一个参数接收。

完整版read命令:

read –p “请输入密码:” –n   6   -t   5   password

echo   -e  “\npassword is $password”

参数说明:

-p:输出提示文字

-n 输入字符串长度,达到此长度,自动结束

-t 输入限制时间

-s 隐藏输入内容

如:

read -p "请输入用户名要求:长度为6输入时间限制为5秒:" -n 6 -t 5 username
read -p "请输入密码:隐藏" -s password
echo "你输入的用户名:${username}和${password}"

运行结果:


2.expr命令:

用途:对整数型变量进行算术运算

如:

expr  3  +  5
expr   3  \* 5

运行结果:


复杂计算需要使用反单引号“·”,对应键盘上“1”旁边的键

如:

expr ` expr 5  -  1`   /   2
num=` expr   $var  /  2`

运行结果:


简便写法:

格式$[表达式]

如:

#简便方法实现运算
num=$[5 - 1]
echo $num
var=4
num=$[var /2]
echo $num

3.echo命令:

用途:用于字符串的输出显示

普通输出,直接使用echo “想要输出的内容”

开启转移:-e

如:

echo It is a test file
echo "hello \n world"
echo -e "hello \n world"
echo -e "helloworld \c"

运行结果:

Shell脚本编程_第3张图片

转义字符说明:

\n #换行

\c #不换行

注:不加-e转义字符没有转义效果
4.printf命令

用途: 与echo功能相同,用来输出,功能更强大,如格式化字符串,指定字符串的宽度,左右对齐方式,默认不换行,可以手动添加\n

语法:

printf format-string [arguments……...]

说明:

format-string: 格式控制字符串

arguments: 参数值列表

%d  %s %c  %f 格式替代符详解:

d: Decimal 十进制整数 -- 对应位置参数必须是十进制整数,否则报错!

s: String 字符串 -- 对应位置参数必须是字符串或者字符型,否则报错!

c: Char 字符 -- 对应位置参数必须是字符串或者字符型,否则报错!

f: Float 浮点 -- 对应位置参数必须是数字型,否则报错!

也有其他的替代符,读者需自行查找

如:

printf "HelloWorld"
printf "HelloWorld\n"
printf "%-20s %-16s %-8s\n" 姓名 性别 体重kg
printf "%-20s %-16s %-8.2f\n" 张三 男 45.1234
printf "%-20s %-16s %-8.2f\n" 李四 男 76.5678
printf "%-20s %-16s %-8.2f\n" 王五 女 40.6542

运行结果:

Shell脚本编程_第4张图片

转义字符说明:

转义序列  意义
\b  退格
\f  换页显示
\n  换行
\r  回车
\t  水平制表符
\v  垂直制表符
\\  转义斜杠
5.test命令

用途:用于测试变量是否相等、是否为空、文件类型等

格式:

test    测试条件

测试范围:整数测试,字符串测试,文件测试

①整数测试

test  v1  -eq  v2 测试两个数是否相等

test  v1  -gt  v2 测试是否 v1>v2

test  v1  -ge  v2 测试是否 v1>=v2

test  v1  -lt  v2 测试是否 v1

test  v1  -le  v2 测试是否 v1<=v2

test  v1  -ne  v2 测试两个数是否不相等

②字符串测试

test  str1=str2 测试字符串是否相等

test  str1!= str2 测试字符串是否不相等

test  str1 测试字符串是否不为空

test  -n  str1 测试字符串是否不为空

test  -z  str1 测试字符串是否为空

③文件测试

test  -d  file 指定文件是否目录

test  -f   file 指定文件是否是常规文件

test  -x  file 指定文件是否可执行

test  -r  file 指定文件是否可读

test  -w  file 指定文件是否可写

test  -a  file 指定文件是否存在

test  -s  file 文件的大小是否非0

另注:通常test命令不单独使用,而是与if语句连用

f)流程控制语句

1.if -else分支结构

if语法格式:

if       条件

then

逻辑块(通常都是些命令)

fi

如:

#如果4<5,输入出4<5
if test 4 -lt 5
then 
	echo "4<5"
fi

运行结果:


写成一行:适用于终端写法

 If   条件; then   逻辑 ; fi


If else语法格式:

if       条件

then

逻辑块(通常都是些命令)

else

逻辑块

fi

如:

read -p "请输入一个数字:" num
if test $num -gt 100
then 
	echo "你中奖了"
else
	echo "你没有中奖"
fi
运行结果

Shell脚本编程_第5张图片

test命令的关键字也是可以省略的

如:

read -p "请输入一个数字:" num

if [ $num -gt 100 ]

then

echo "你中奖了(省略了test关键字)"

else

echo "你没中奖(省略了test关键字)"

fi

运行结果:

Shell脚本编程_第6张图片

If else-if else语法格式:

if       条件1

then

逻辑块(通常都是些命令)

elif 条件2

then

逻辑块

else

逻辑块

fi

如:

read -p "请输入学生姓名:" name 
read -p "请输入成绩:" score
if [ $score -ge 90 ]
then 
	echo "${name} is A"
elif [ $score -ge 80 ]
then 
	echo "${name} is B"
elif [ $score -ge 70 ]
then 
	echo "${name} is C"
else 
	echo "${name} is D"
fi 

运行结果:

Shell脚本编程_第7张图片

①分支结构也可以进行嵌套

②多个条件的联合

-a   逻辑与    当两个条件都成立时,结果为true

-o   逻辑或,两个条件只要有一个成立,结果为真

2.case多选择语句

Case语句会匹配一个值和一个模式,匹配成功,执行相应逻辑块

语法:

case  值   in

模式1)

逻辑块

;;

模式2)

逻辑块

;;

esac

如:

read -p "输入一个选择:" num
case $num in
1)
	echo "1"
;;
a)
	echo "a"
;;
aa)
	echo "aa"
;;
"bb")
	echo "bb"
;;
*)
	echo "other"
;;
esac

运行结果:

Shell脚本编程_第8张图片

注:没有的选项就会匹配到*)这个选项

3.for循环结构

格式:

for   变量 in   名字表

do

逻辑

done

写成一行:

for   变量    in  列表;do  逻辑; done;

如:

for day in Sunday Monday Tuesday WednesDay Thursday Friday SaturDay

do

echo $day

done

运行结果:

Shell脚本编程_第9张图片

4.select表单循环

语法:

select  变量  in   列表

do

逻辑块

done

写成一行:

Select  变量 in 列表;do  逻辑   ; done;

如:

#输出/home/llg目录下的路径名称:
select pathName in `ls /home/llg`
do
	echo "路径名称:$pathName"
done

运行结果:

Shell脚本编程_第10张图片

注:这个要使用Ctrl+c退出

5.while循环

语法:

while  条件

do

逻辑块

done

写成一行:

Select  变量 in 列表;do  逻辑   ; done;

如:

#使用while循环输出1到10
num=0
while [ $num -le 9 ]
do 
	echo $[++num]
done

运行结果:

Shell脚本编程_第11张图片

6.无限循环

语法:

while :

do

逻辑块

done

while true

Do

逻辑块

done

如:

while true
do
        echo "HelloWorld"
done

运行结果:

Shell脚本编程_第12张图片

7.until循环

until循环执行逻辑直到true时停止

语法:

util 条件

do

逻辑块

done 

如:

#输出1到10
#!/bin/bash
num=0
until [ $num -gt 9 ]
do
        let  “num++”
        echo $num
done 

运行结果:

Shell脚本编程_第13张图片

8.break与continue

①如果想要提前结束循环(即想跳出循环)

使用关键字

break

如:

#循环输出1到10,当输出到五的时候跳出循环

for i in {1..10}
do
        echo $i
        if [ $i -eq 5 ]
        then
                break
        fi
done

运行结果:

Shell脚本编程_第14张图片

②如果想结束当次循环,进行下一次循环

使用关键字

continue

如:

#输出1到10中的偶数

for i in {1..10}
do
        if [ $[i % 2] -eq 1 ]
        then
                continue
        fi
        echo "i:${i}"
done

运行结果:

Shell脚本编程_第15张图片

9.shift指令

作用:参数左移,每执行一次,参数序列顺次左移一个位置,$#的值少一个,

多数用于分别处理每一个参数,移出去的参数不再可用

如:

#使用位置参数显示1~7
while [ $# -ne 0 ]
do
echo "输入参数:$1"
shift
done

运行结果:

Shell脚本编程_第16张图片

g)函数:

1.基础:

在shell脚本中,我们也可以定义函数,

封装逻辑,进行调用

语法:

[function]       functionName()

{

逻辑

}

调用时,不带()

说明:

1、关键字function,可以忽略不写

如:

demoFun(){
echo "这是我的第一个 shell 函数!"
}
echo "-----函数开始执行-----"
demoFun
echo "-----函数执行完毕-----"

运行结果:


2.return关键字(可加可不加,看需求)

      (1)加:    返回值只能是0~255的数字

      (2)不加:  最后一条命令结果作为返回值

如:

funWithReturn(){
    echo "这个函数会对输入的两个数字进行相加运算..."
    echo "输入第一个数字: "
    read aNum
    echo "输入第二个数字: "
    read anotherNum
    echo "两个数字分别为 $aNum 和 $anotherNum !"
return $(($aNum+$anotherNum))
}
funWithReturn
echo "输入的两个数字之和为 $? !"

运行结果:

Shell脚本编程_第17张图片

 注意:

①函数内的变量,都是全局变量(整个脚本都可以使用)

②函数的参数:调用函数时,可以向函数中传递参数,必须通过$n的形式来获取参数的值

如:

funWithParam(){
    echo "第一个参数为 $1 !"
    echo "第二个参数为 $2 !"
    echo "第十个参数为 $10 !"
    echo "第十个参数为 ${10} !"
    echo "第十一个参数为 ${11} !"
    echo "参数总数有 $# 个!"
    echo "作为一个字符串输出所有参数 $* !"}
funWithParam 1 2 3 4 5 6 7 8 9 34 73

运行结果:

Shell脚本编程_第18张图片

另外

变量名称  意义
$*  获取所有参数,合并成一个字符串
$#  参数个数
$$  当前进程的ID
$!  后台运行的最后一个进程的ID
$?  显示最后一个命令的退出状态。0表示没有错误

h)Shell输出/输入重定向

通常情况下:系统命令会从你的终端接收输入数据,再将需要输出的信息发送给你的终端。

有时:我们可能会需要指定 输入的位置,或者指定输出的位置,这就是”重定向”的概念

命令  说明
command > file  将输出重定向到file
command < file  将输入重定向到file
command >> file  将输出以追加的方式重定向到file

①输出重定向会覆盖文件内容,如:

$ echo "HelloWorld" > users
$ cat users

运行结果:


②如果不希望文件内容被覆盖,可以使用 >> 追加到文件末尾,如:

$ echo " HelloWorld" >> users
$ cat users

运行结果:


③输入从定向,如:

read name < ./users

echo $name

运行结果:


i)Shell文件引入外部脚本

概念:

Shell脚本内部也可以引用其他外部脚本这样可以很方便的封装一些公用的代码作为一个独立的文件

Shell脚本引用外部脚本语法如下:

.    fileName      #   .与文件之间一定要有空格

或者

source    fileName

如:

创建两个 shell 脚本文件。

test1.sh 代码如下:

#!/bin/bash
name=”HelloWorld”

test2.sh 代码如下:

#!/bin/bash
#使用 . 号来引用test1.sh 文件. ./test1.sh
# 或者使用以下包含文件代码# source ./test1.sh
source ./test1.sh
echo "大声说::$name

运行结果:


j)Shell脚本测试

sh  -x  script

这将执行该脚本并显示所有变量的值

如:

sh -x test2.sh

运行结果:

Shell脚本编程_第19张图片

②sh  -n  script

不执行脚本只是检查语法的模式,将返回所有语法错误

如:
source ./test1.sh
echo "大声说::$name

我们少些了双引号的另一边

运行结果:

k)awk命令应用

awk  -F  域分隔符 ‘命令’

如:

检测系统中UID0的用户

awk –F: ‘$3==0 {print $1}’ /etc/passwd

运行结果:


检测系统中密码为空的用户

awk F: length($2)==0 {print $1}/etc/shadow

因为我们有密码为空的用户所以输出结果为空

你可能感兴趣的:(Linux)