SHELL脚本

SHELL脚本

打开文本编辑器,新建一个文本文件,并命名为test.sh。
扩展名sh代表shell,扩展名并不影响脚本执行,见名知意就行
hello wold

//An highlighted block
  1 #!/bin/bash
  2 echo "Hello world!"                      

在这里插入图片描述
第一行的#!是一个约定的标记,它告诉系统这个脚本需要什么解释器来执行,即哪一种shell;后面的/bin/bash就是指明了解释器的具体位置。
第二行的echo命令用于向标准输出设备(standard output ,stdout一般就是指显示器)输出文本。在.sh文件中使用命令与在终端直接输入命令的效果是一样的。
shell脚本中所有以#开头的都是注释(当然#!除外),写脚本的时候,多写注释是非常有必要的,以方便其他人能看懂你的脚本,也方便后期自己维护时看懂自己的脚本

//An highlighted block
  1 #!/bin/bash
  2 #输出并读取用户的输入
  3 #再输出
  4 echo "What is your name?"
  5 read Person
  6 echo "Hello,$Person"                     

在这里插入图片描述read命令用来从标准输入文件(standard input,stdin,一般就是指键盘)读取用户数的数据
read Person表示从终端读取用户数的数据,并赋值给Person变量

输出变量Person的内容,注意要在变量名前加上$,否则变量名会作为字符串的一部分处理

将shell脚本作为程序运行
shell脚本也是一种解释执行的程序,可以在终端直接调用(需要使用chmod命令给shell脚本上加上执行权限)
chmod u+x 表示给hello_read.sh增加其用户的执行权限
./表示当前目录,整条命令的意思是执行当前目录下的.hello_read.sh脚本,如果不写./,Linux会到系统路径(由PATH环境变量指定)下查找,而系统路径下显然不存在这个脚本,所以会执行失败
通过这种方式运行脚本,脚本文件的第一行#!/bin/bash一定要写对,好让系统查找到正确的解释器
将shell脚本作为参数传递给bash解释器
你也可以直接运行bash解释器,将脚本文件的名字作为参数传递给bash
在这里插入图片描述通过这种方式运行脚本,不需要在脚本文件的第一行指定解释器信息,写了也没有用
更加简洁的写法是运行bash命令,bash是一个外部命令,shell会在/bin目录中找对应的应用程序,也即/bin/bash
在这里插入图片描述这两种的写法本质上是一样的,第一种给出了绝对路径,会直接运行bash解释器;第二种写法通过bash命令找到bash解释器所在的目录,然后再运行,只不过多了一个查找的过程而已
检查是否开启了新进程

//An highlighted block
  1 #!/bin/bash
  2 echo $$

在这里插入图片描述linux中的每一个进程都有一个唯一的PID,使用 变量就可以获取当前的进程 I D , 变量就可以获取当前的进程ID, 变量就可以获取当前的进程ID是shell中的特殊变量

当前进程中运行shell脚本
这里需要引入一个新的命令------source命令。source是shell内置命令的一种,它会读取脚本文件中的代码,并依次执行所有的语句。你可以理解为,source命令会强制执行脚本文件中的全部命令,而忽略脚本文件的权限。
source命令的用法为:
source filename
也可以简写为:
. filename
两种写法效果相同,对于第二种写法,注意点号和文件名中间有一个空格
例如,运行hello_world
在这里插入图片描述使用source命令不用给脚本增加执行权限,并且写不写./都行。

检测是否在当前进程中
在这里插入图片描述进程的PID都是一样的,当然是同一个进程了。

如果需要在新的进程中运行shell脚本,一般使用bash test.sh这种写法;如果在当前进程中运行shell脚本,一般使用./test.sh

shell变量
变量是任何一门编程都必不可少的部分,变量用来存放各种数据,脚本语言在定义变量时通常不需要指明类型,直接赋值就可以,shell变量也遵循这个规则。
在bash shell中,每一个变量的值都是字符串,无论你给变量赋值时有没有使用引号,值都会以字符串的形式存储。
这意味着,bash shell在默认情况下不会区分变量类型,即使你将整数和小数赋值给变量,它们也会被视为字符串,这一点和大部分编程语言不同。
当然。如果有必要,你也可以使用shell declare 关键字显示定义变量的类型,但在一般情况下没有这个需求,shell开发者在编写代码是自行注意值得类型即可。
定义变量
shell支持一下三种定义变量的方式
variable=value
variable=‘value’
variable=“value”

variable是变量名,value是赋给变量的值。如果value不包含任何空白字符(例如空格、Tab缩进等),那么可以不使用引号;如果value包含了空白符,那么必须使用引号包含起来。
注意,赋值号=的周围不能有空格,这和其他的大部分编程语言不一样
shell变量的命名规范和大部分编程语言都一样:
1.变量名有数字、字母、下划线组成
2.必须以字母或者下划线开头
3.不能使用shell里的关键字(通过help命令可以查看保留字)
变量定义和使用举例:

//An highlighted block
  1 #!/bin/bash
  2 url=http://www.bau.com
  3 echo $url
  4 name='百度'
  5 echo $name
  6 user="张三"
  7 echo $user
  8 echo ${user}
  9 echo "i am ${user}user"

使用一个定义过的变量,只要在变量名前面加上美元符号 即可,变量名外面的花括号是可选的,加不加都行,加花括号是为了帮助解释器识别变量的边界,㘝不给 u s e r 变量名加花括号,解释器就会把 即可,变量名外面的花括号是可选的,加不加都行,加花括号是为了帮助解释器识别变量的边界,㘝不给user变量名加花括号,解释器就会把 即可,变量名外面的花括号是可选的,加不加都行,加花括号是为了帮助解释器识别变量的边界,不给user变量名加花括号,解释器就会把useruser当成一个变量(其值为空),代码执行的结果就不是我们所期望的。
在这里插入图片描述修改变量的值
已定义的变量,可以直接被重新赋值

//An highlighted
1 #!/bin/bash
  2 url=http://www.bau.com
  3 echo $url
  4 name='百度'
  5 echo $name
  6 user="张三"
  7 echo $user
  8 echo ${user}
  9 echo "i am ${user}user"
 10 user="李四"
 11 echo $user

单引号和双引号的区别

//An highlighted block
  1 #!/bin/bash
  2 url="www.baidu.com"
  3 website1='百度的网址:${url}'
  4 website2="百度的网址:${url}"
  5 echo $website1
  6 echo $website2

在这里插入图片描述以单引号’'包围变量的值时,单引号里面是什么就输出什么,即内容中有变量和命令(命令需要反引起来)也会把它们原样输出,这种方式比较适合定义显示纯字符串的情况,即不希望解析变量、命令等的场景。
以双引号“”包围变量的值时,输出会先解析里面的变量和命令,而不是把双引号中的变量名和命令原样输出。这种方式比较适合字符串中附带有变量和命令并且想讲其解析后再输出的变量定义。
如果变量的内容是数字,那么可以不加引号;如果真的需要原样输出就加单引号;其他没有他别要求的字符串等最好都加上双引号,定义变量时加双引号是最常见的使用场景

将命令的结果赋值给变量
shell也支持将命令的执行结果赋值给变量,常见的有一下两种方式:
variable=`command`
variable= ( c o m m a n d ) 第一种方式把命令用反引号 ‘ ‘ ( 位于 e s c 键下方 ) 包围起来,反引号和单引号非常相似,容易弄混淆,所以不推荐这种方式;第二种方式把命令用 (command) 第一种方式把命令用反引号``(位于esc键下方)包围起来,反引号和单引号非常相似,容易弄混淆,所以不推荐这种方式;第二种方式把命令用 (command)第一种方式把命令用反引号‘‘(位于esc键下方)包围起来,反引号和单引号非常相似,容易弄混淆,所以不推荐这种方式;第二种方式把命令用()包围起来,区分更加明显,所以推荐使用这种方式。
例如,我们创建一个名为log.txt的文本文件,用来记录日常工作。使用cat命令将log.txt的内容读取出来,并且赋值给一个变量,然后使用echo命令输出。

//An highlighted block
[gyg@softeem bash]$ vi log.txt
[gyg@softeem bash]$ log=$(cat log.txt)
[gyg@softeem bash]$ echo $log
大家好,这是我的linux笔记
[gyg@softeem bash]$ log=`cat log.txt`
[gyg@softeem bash]$ echo $log
大家好,这是我的linux笔记

只读变量
使用readonly命令可以将变量定义为只读变量,只读变量的值不能被改变
下面的例子尝试更改只读变量,会报错

//An highlighted block
  1 #!/bin/bash
  2 user="张三"
  3 readonly user
  4 user="李四"

在这里插入图片描述删除变量
使用unset命令可以删除变量,语法:
unset variable_name
变量删除后不能再次使用;unset命令不能删除只读变量

//An highlighted block
  1 #!/bin/bash
  2 user="张三"
  5 unset user
  6 echo $user

上面的脚本没有任何输出

shell变量的作用域
shell变量的作用域可以分为三种:
1.有的变量只能在函数内部使用,这叫做局部变量(local variable)
2.有的变量可以在当前shell进程中使用,这叫做全局变量(global variable)
3.而有的变量可以在子进程中使用,这叫做环境变量(environment variable)
shell也支持自定义函数,但是shell函数和其他编程语言的一个不同点就是:在shell函数定义中定义的变量默认也是全局变量,它和在函数外部定义的变量拥有一样的效果

//An hightlighted block
  1 #!/bin/bash
  2 #定义函数
  3 function func(){
  4 a=99
  5 }
  6 
  7 #调用函数
  8 func
  9 
 10 
 11 #输出函数内部的变量
 12 echo $a

在这里插入图片描述a在函数内部定义的,但是在函数外部也可以得到它的值,指明它的作用域是全局的,而不是仅限于函数内部。
要想变量的作用域仅限于局部内部,可以在定义时加上local 命令,此时该变量就成了局部变量。

//An hightlighted block
  1 #!/bin/bash
  2 #定义函数
  3 function func(){
  4 local a=99
  5 }
  6 
  7 #调用函数
  8 func
  9 
 10 
 11 #输出函数内部的变量
 12 echo $a

在这里插入图片描述输出结果为空,表明变量a在函数外部无效,是一个局部变量。
shell全局变量
所谓全局变量,就是指变量在当前的整个shell进程中都有效,每个shell进程都有自己的作用域,彼此之间互不影响,在shell中定义的变量,默认就是全局变量
想要实际演示全局变量在不同shell进程中的互不相关性,可以在图形界面下打开两个shell
首相打开一个shell窗口,定义一个变量a并赋值99,然后打印,这是在同一个shell窗口中是可以正确打印变量a的值。然后打开另一个shell窗口,同样打印变量a的值,但是结果却为空在这里插入图片描述这说明全局变量a仅仅在定义它的第一个shell进程中有效,对新的shell进程没有影响。
需要强调的是,全局变量的作用范围是当前shell进程,而不是当前的shell脚本文件,它们是不同的概念。打开一个shell窗口就创建了一个shell进程,打开多个shell窗口就创建了多个shell进程,每个shell进程是独立的,拥有不同的进程ID。在一个shell进程中可以使用source命令执行多个shell脚本文件,此时全局变量在这些脚本文件中都有效。

//An highlighted block
  1 #!/bin/bash
  2 echo $a
  3 b=200
//An highlighted block
  1 #!/bin/bash
  2 echo $b

在这里插入图片描述这三条命令都是在一个进程中执行的,使用source命令运行shell脚本。必须在当前进程中运行shell脚本,不能在新的进程中运行shell脚本。

shell环境变量
全局变量只在当前shell进程中有效,对其他shell进程和子进程都无效。如果使用export命令将全局变量导出,那么它就在所有的子进程中也有效了,这称为环境变量。
环境变量被创建时所处的shell进程被称为父进程,如果在父进程中再创建一个新的shell进程来执行shell命令,那么这个新的进程被称作shell子进程。当shell子进程产生时,它会继承父进程的环境变量为自己所用,所以说环境变量可从父进程传递给子进程,还可以传递给孙进程。
注意两个没有父子关系的shell进程是不能传递环境变量的,并且环境变量只能向下传递,不能向上传递。
创建shell子进程最简单的方式是运行bash命令
在这里插入图片描述可以发现a在shell子进程中默认是无效的,使用export将a导出为环境变量后,在子进程中就可以使用了。
export a这种形式是在定义变量a以后再将它导出为环境变量,如果想在定义的同时导出为环境变量,可以写作export a=22
一直强调的是环境变量在shell子进程中有效,并不是在所有的shell进程中都有效,如果通过终端创建了一个新的shell窗口,那它就不是当前shell的子进程,环境变量对这个新的shell进程是无效的。
通过export导出的环境变量只对当前shell进程以及所有的子进程有效,如果最顶层的父进程关闭了,那么环境变量也就消失了,其他的进程也就无法使用了,所以说环境变量也是临时的。
只有将变量写入shell配置文件中,才能让一个变量在所有的shell进程中有效。

shell命令替换
shell命令替换是指将命令的输出结果赋值给某个变量。比如,比如在目录中输入ls命令可以查看当前目录中所有的文件,但如何将输出内容存入某个变量中呢,这就需要使用命令替换了,这也是shell编程中使用非常频繁的功能。
shell中有两种方式可以完成命令替换,一种是反引号``,一种是 ( ) , 使用方法如下: v a r i a b l e = c ˋ o m m a n d s ‘ v a r i a b l e = (),使用方法如下: variable=\`commands` variable= (),使用方法如下:variable=cˋommandsvariable=(commands)
其中,variable是变量名,commands是要执行的命令。commands可以只有一个命令,也可以有多个命令,多个命令之间以分号;分隔
date命令用来获取当前系统的时间

//An highlighted block
  1 #!/bin/bash
  2 begin_time=`date`       #开始时间
  3 sleep 20s               #休眠204 finish_time=$(date)     #结束时间
  5 
  6 echo "Begin time:$begin_time"
  7 echo "Finish_time:$finish_time"

在这里插入图片描述使用date命令的%s格式控制符可以得到当前UNIX时间戳,这样就可以直接计算脚本运行时间了,UNIX时间戳是指从1970年1月1日00:00:00到目前为止的秒数。

//An highlighted block
  1 #!/bin/bash
  2 #begin_time=`date`      #开始时间
  3 #sleep 20s              #休眠204 #finish_time=$(date)        #结束时间
  5 
  6 #echo "Begin time:$begin_time"
  7 #echo "Finish_time:$finish_time"
  8 
  9 
 10 
 11 
 12 begin_time=`date +%s`
 13 sleep 20s
 14 finish_time=$(date +%s)
 15 run_time=$((finish_time - begin_time))
 16 
 17 echo "begin time:$begin_time"
 18 echo "finish time:$finish_time"
 19 echo "run  time:$run_time"   
                          

在这里插入图片描述run_time后面的(())是shell数学计算命令,和其他语言不同,在shell中进行数据计算不那么方便,必须使用专门的数学计算命令,(())就是其中之一。

注意,如果被替换的命令输出内容包括多行(也即有换行符),或者含有多个连续的空白符,那么在输出变量时应该将变量名用双引号包围,否则系统会使用默认的空白符来填充,这会导致换行无效,以及连续的空白符被压缩成一个。

//An highlighted block
  1 #!/bin/bash
  2 lsl=`ls -l`
  3 echo $lsl           #不使用双引号包围
  4 echo "-------------------------"        #输出分隔符
  5 echo "$lsl"

在这里插入图片描述原则上讲,上面提到的两种变量替换的形式是等价的,可以随便使用;但是,反引号毕竟看起来像单引号,有时候会对查看代码造成困扰,而使用 ( ) 就相对清晰,能有效避免这种混乱。而且有些情况必须使用 ()就相对清晰,能有效避免这种混乱。而且有些情况必须使用 ()就相对清晰,能有效避免这种混乱。而且有些情况必须使用(); ( ) 支持嵌套,反引号不行。要注意的是, ()支持嵌套,反引号不行。 要注意的是, ()支持嵌套,反引号不行。要注意的是,()仅在bash shell中有效,而反引号可在多种shell中使用。所以这两种命令替换的方式各有特点,究竟选择哪种方式全看个人需求。

shell位置参数
运行shell脚本的时候,我们可以给他传递一些参数,这些参数在脚本文件内部可以使用$n的形式接受,例如,$1表示第一个参数, 2 表示第二个参数,以此类推。同样的,在调用函数时也可以传递参数, s h e l l 函数参数的传递和其他编程语言不同,没有所谓的形参和实参,在定义函数时也不用指明参数的名字和树木,换句话说,定义 s h e l l 函数时不能带参数,但是在调用函数时却可以传递参数,这些传递进来的参数,在函数内部也使用 2表示第二个参数,以此类推。 同样的,在调用函数时也可以传递参数,shell函数参数的传递和其他编程语言不同,没有所谓的形参和实参,在定义函数时也不用指明参数的名字和树木,换句话说,定义shell函数时不能带参数,但是在调用函数时却可以传递参数,这些传递进来的参数,在函数内部也使用 2表示第二个参数,以此类推。同样的,在调用函数时也可以传递参数,shell函数参数的传递和其他编程语言不同,没有所谓的形参和实参,在定义函数时也不用指明参数的名字和树木,换句话说,定义shell函数时不能带参数,但是在调用函数时却可以传递参数,这些传递进来的参数,在函数内部也使用n的形式接受,例如,$1表示第一个参数, 2 表示第二个参数,以此类推。这种通过 2表示第二个参数,以此类推。 这种通过 2表示第二个参数,以此类推。这种通过n的形式来接受的参数,在shell中称为位置参数。
在讲解变量命名时,变量的名字必须以字母或者下划线开头,不能以数字开头;但是位置参数却偏偏是数字,这和变量的命名规则是相搏的,所以我们将它们视为“特殊变量”。
给脚本文件传递位置参数

//An highlighted block
  1 #!/bin/bash
  2 echo "Language:$1"
  3 echo "URL:$2"

在这里插入图片描述
其中english是第一个位置参数,www.baidu.com是第二个位置参数,两者之间以空格分隔。

给函数传递位置参数

//An highlighted block
  1 #!/bin/bash
  2 #定义函数
  3 function func(){
  4     echo "Language:$1"
  5     echo "URL:$2"
  6 }
  7 
  8 #调用函数
  9 func english www.baidu.com

在这里插入图片描述注意事项:
如果参数太多,达到或者超过了10个,那么就得使用 n 的形式来接受了,例如 {n}的形式来接受了,例如 n的形式来接受了,例如{10}、${33}。{}的作用是为了帮助解释器识别参数的边界,这就跟使用变量时加{}时一样的效果。

shell特殊变量
在这里插入图片描述给脚本文件传递参数

//An highlighted block
  1 #!/bin/bash
  2 
  3 echo "Process ID:$$"
  4 echo "File name:$0"
  5 echo "First Parameter:$1"
  6 echo "Second Parameter:$2"
  7 echo "All Parameters 1:$@"
  8 echo "All Parameters 2:$*"
  9 echo "Total:$#"

在这里插入图片描述给函数传递参数

//An highlighted block
  1 #!/bin/bash
  2 
  3 #定义函数
  4 function func(){
  5     echo "Language:$1"
  6     echo "URL:$2"
  7     echo "First Parameter:$1"
  8     echo "Second Parametor:$2"
  9     echo "All parameters:$@"
 10     echo "All parameters:$*"
 11     echo "Total:$#"
 12 }
 13 
 14 #调用函数
 15 func c++ www.baidu.com

在这里插入图片描述
*shell$和shell$@的区别
∗ 和 *和 @都表示传递给函数或者脚本的所有参数
∗ 和 *和 @不被双引号包围时,它们没有任何区别,都是将接受的每个参数看做一份数据,彼此以空格来隔开
但是当它们被双引号包围时,就会有区别了:
"$"会将所有的参数从整体上看做一份数据,而不是把每个参数都看作一份数据
"$@“仍然将每个参数看做一份数据,彼此之间是独立的。
比如传递了5个参数,那么对于”$
“来说,这5个参数会合并到一起形成一份数据,它们之间是无法分隔的;而对于”$@“来说,这5个参数是相互独立的,它们是5份数据。
如果使用echo直接输出”$*“和”$@"做对比,是看不出来区别的,但是如果使用for循环来逐个输出数据,立即就能看出区别来。

//An highlighted block
  1 #!/bin/bash
  2 echo "print each param from \"\$*\""
  3 for var in "$*"
  4 do
  5     echo "$var"
  6 done
  7 
  8 echo "print each param from \"\$@\""
  9 for var in "$@"
 10 do
 11     echo "$var"
 12 done

在这里插入图片描述
从运行的结果可以发现,对于" ∗ " ,只循环了一次,因为它只有 1 份数据;对于 " *",只循环了一次,因为它只有1份数据;对于" ",只循环了一次,因为它只有1份数据;对于"@",循环执行了5次,因为它有5份数据

shell $?获取函数返回值或者上一个命令的退出状态
$?是一个特殊变量,用来获取上一个命令的退出状态或者上一个函数的返回值
所谓退出状态,就是上一个命令执行后的结果,退出状态是一个数字,一般情况下,大部分命令执行成功会返回0,失败返回1,这和c语言的main函数时类似的。
不过,也有一些命令返回其他值,表示不同类型的错误

//An highlighted block
  1 #!/bin/bash
  2 if [ "$1" == 100 ]
  3 then
  4     exit 0
  5 else
  6     exit 1
  7 fi

在这里插入图片描述exit表示退出当前shell进程,我们必须使用bash 新建进程运行,否则当前shell会话(终端)会被关闭,e我们就无法取得他的退出状态了。
注意if语句后面的[ ]u与两边都有空格隔开。

$?获取函数的返回值

//An highlighted block
  1 #!/bin/bash
  2 
  3 #得到两个数相加的和
  4 function add(){
  5     return `expr $1 + $2`
  6 }
  7 
  8 add 23 50
  9 echo $?

在这里插入图片描述
严格来说,shell函数中的return关键字用来表示函数的退出状态,而不是函数的返回值;shell不想其他编程语言,没有专门处理返回值的关键字,以上的处理方案,在shell中是非常错误的,shell函数的返回值和其他编程语言大有不同

shell字符串详解
字符创就睡一系列字符的组合,字符串是shell编程中最产常用的数据类型之一(除了数字和字符串也没有其他类型了)。
字符串可以用单引号包围,也可以用双引号包围,也可以不用引号,它们之间是有区别的。

字符串举例:
str1=shell.shell
str2=“shell bash”
str3=‘linux_shell’

下面我们说一下三种形式的区别:
1:由单引号‘’包围的字符串,任何字符串都会原样输出,在其中使用变量是无效的;字符串中不能出现单引号,即使对单引号转义也不行。
2:有双引号“”包围的字符串,如果其中包含了某个变量,那么该变量会被解析(得到该变量的值),而不是原样输出;字符串中可以出现双引号,只要它被转义了就行。
3:不被引号包围的字符串,不被引号的字符串中出现变量时也会被解析,这一点和双引号包围的字符串一样,字符串中不能出现空格,否则空格后面的字符串会被作为其他变量或者命令解析

//An highlighted block
  1 #!/bin/bash
  2 
  3 n=15
  4 str1=shell.bash.a.a$n str2="shell \"script\" $n"
  6 str3='www.baidu.com $n'
  7 
  8 echo $str1
  9 echo $str2
 10 echo $str3

在这里插入图片描述
str1中包含了 n ,他被解析为变量 n 的引用。 n,他被解析为变量n的引用。 n,他被解析为变量n的引用。n后面有空格,紧随空格的是str2,shell将str2解释为一个新的变量名,而不是作为字符串str1的一部分。
str2中包含了引号,但是被转义了(有反斜杠\开头的表示转义字符),str2中也包含了 n ,它也被解析为变量 n 的引用。 s t r 3 中也包含了 n,它也被解析为变量n的引用。 str3中也包含了 n,它也被解析为变量n的引用。str3中也包含了n,但是仅仅是作为普通字符,并没有解析为变量n的引用。
获取字符串的长度
在shell中获取字符串的长度很简单:
${#string_name}
string_name 表示字符串的名字

//An highlighted block
  1 #!/bin/bash
  2 
  3 n=15
  4 str1=shell.bash.a.a$n str2="shell \"script\" $n"
  5 str3='www.baidu.com $n'
  6 
  7 echo $str1
  8 echo $str2
  9 echo $str3
 10 
 11 
 12 echo ${#st1}
 13 echo ${#str2}
 14 echo ${#str3}

在这里插入图片描述字符串的拼接
在脚本语言中,字符串的拼接(也称字符串连接或者字符串合并)往往都非常简单,例如:
在PHP中,使用.即可连接两个字符串
在JavaScript中,使用+即可将两个字符串合并为一个。
然而在shell中不需要任何运算符,将两个字符串并排放在一起就能实现拼接,非常简单粗暴。

//An highlighted block
  1 #!/bin/bash
  2 
  3 name="shell"
  4 url="www.baidu.com"
  5 
  6 str1=$name$url          #中间不能有空格
  7 str2="$name $url"       #如果被双引号包围,那么中间可以有空格
  8 str3=$name":"$url       #中间可以出现别样的字符串
  9 str4="$name:$url"       #这样写也可以
 10 str5="${name}bash:${url}index.html"         #这个时候需要给变量名加上大括号
 11 
 12 echo $str1
 13 echo $str2
 14 echo $str3
 15 echo $str4
 16 echo $str5

在这里插入图片描述对于第6行, n a m e 和 name和 nameurl之间之所以不能出现空格,是因为当字符串不被任何一种引号包围时,遇到空格就结束了,空格后面的内容会作为其他变量或者命令解析;对于第10行代码,加{}是为了帮助解释器识别变量的边界。

字符串截取
shell字符串截取通常有两种方式:从指定位置开始截取和从指定字符(子字符串)开始截取。

从指定位置截取
这种方式需要两个参数:除了指定起始位置,还需要截取长度,才能最终确定需要截取的字符串。
既然需要指定起始位置,那么就涉及到计数方向的问题,到底是从字符串左边开始计数,还是从字符串右边开始计数,shell同事支持两种计数方式。
1)从字符串左边开始计数
如果想从字符串左边开始计数,那么截取字符串的具体格式如下:
${string:start:length}
其中string是要截取的字符串,start是起始位置(从左边开始,从0开始计数),length是要截取的长度(省略的话表示知道字符串的末尾)
例如:

//An highlighted block
  1 #!/bin/bash
  2 url="http://www.baidu.com"
  3 echo ${url:7:3}

在这里插入图片描述

//An highlighted block
  1 #!/bin/bash
  2 url="http://www.baidu.com"
  3 echo ${url:7:3}
  4 echo ${url:7}           #省略length,截取到字符串末尾

在这里插入图片描述2)从右边开始计数
如果想从字符串的右边开始计数。那么截取字符串的具体格式如下:
${string:0-start:length}
同第1中相比,第2中格式仅仅多了0-,这是固定的写法,专门用来表示从字符串的右边开始计数。
这里需要强调两点:
从左边开始计数时,起始数字是0(这很符合程序员的思维);从右边开始计数时,起始数字是1(符合常人的思维),计数方向不同,起始数字也不同。
不管从那边开始计数,截取方向都是从左到右。

//An highlighted
  1 #!/bin/bash
  2 
  3 url="http://www.baidu.com"
  4 echo ${url:0-3:3}

在这里插入图片描述

//An highlighted block
  1 #!/bin/bash
  2 
  3 url="http://www.baidu.com"
  4 echo ${url:0-3:3}
  5 echo ${url:0-13}

在这里插入图片描述从指定字符(子字符串)开始截取
这种截取方式无法指定字符串长度,只能从指定字符(子字符串)截取到字符串末尾,shell可以截取指定字符(子字符串)右边所有字符,也可以截取左边的所有字符。
1)使用#截取右边字符
使用#号可以截取指定字符(或者子字符串)右边的所有字符,具体格式如下:
${string#*chars}
其中,string表示要截取的字符,chars是指定的字符(或者子字符串),*是通配符的一种,表示任意长度的字符串。*chars连起来使用的意思是:忽略左边的所有字符,知道遇到chars(chars不会被截取)。

//An highlighted
  1 #!/bin/bash
  2 
  3 url="www.baidu.com:shell:bash"
  4 echo ${url#*:}
  5 echo ${url#*m:}
  6 echo ${url#*com:}           #shell碰到第一个匹配的字符(子字符串)就会结束
  7 
  8 echo ${url#bai}             #不加*结果还是原来的网址
  9 echo ${url#www.bai}         #但是还可以把左边的全部写进来 就会截取左边的
 10 
 11 echo ${url##*:}             #可以输入两个##会匹配到最后一个匹配的字符

在这里插入图片描述使用%截取左边字符
使用%号可以截取指定字符(或者子字符串)左边的所有字符,具体格式如下:
${string%char*}
请注意*的位置,因为要截取char左边的字符,而忽略右边的字符,所以*的位置应该位于chars的右侧,其他方面%和#的用法相同。

//An highlighted
  1 #!/bin/bash
  2 
  3 url="www.baidu.com:shell:bash"
  4 echo ${url%.*}
  5 echo ${url%%.*}

在这里插入图片描述
在这里插入图片描述在这里插入图片描述shell数组
和其他编程语言一样,shell也支持数组,数组(array)是若干数据的集合,其中的每一份数据都称为元素。
shell并没有限制数组的大小,理论上可以存放无限量的数据。和其他编程语言类似,shell数组元素的下标也是从0开始计数。
获取数组中的元素要使用下标[ ],下标可以是整数,也可以是一个结果为整数的表达式,当然,下标必须大于等于0.
遗憾的是,常用的bash shell只支持一维数组,不支持多维数组。

shell数组的定义
在shell中,用括号()来表示数组,数组元素之间用空格来分隔。由此,定义一维数组的一般形式为:
array_name=(ele1 ele2 ele3…ele)
注意,赋值号=两边不能有空格,必须紧挨着数组名和数组元素。
num=(1 2 3 4 5 6 7 8 9)
shell是弱类型的,它并不要求所有数组元素的类型必须相同,例如:
arr=(20 78 “www.baidu.com”)
第三个元素就是一个异类,前面两个元素是整数,而第三个元素是字符串。
shell数组的长度不是固定的,定义之后还能增加元素,例如,对于上面的数组长度是3,使用下面代码会在最后面增加一个元素,长度变成4:
arr[3]=44

此外,无需逐个元素的给数组赋值,下面代码就是只给特定元素赋值:
array=([1]=24 [3]=33 [9]=22)
以上代码就只给第2、4、10个元素赋值,所以数组的长度是3。
获取数组元素
获取数组元素的值,一般使用下面的格式
a r r a y n a m e [ i n d e x ] 其中, a r r a y n a m e 是数组名, i n d e x 是下标。 n = {array_name[index]} 其中,array_name是数组名,index是下标。 n= arrayname[index]其中,arrayname是数组名,index是下标。n={arr[1]}
表示获取arr的第2个元素,然后赋值给变量n

再如:
echo ${arr[3]}
表示输出数组arr的第4个元素

使用@或者*可以获取数组中的所有元素
${nums[@]}
${nums[*]}
两者都可以得到nums数组的所有元素。

//An highlighted block
  1 #!/bin/bash
  2 
  3 num=(1 2 3 4 5 6 7 8 9)
  4 echo ${num[@]}
  5 
  6 num[9]=10
  7 echo ${num[*]}
  8 echo ${num[4]}

在这里插入图片描述

shell数组的长度
所谓数组的长度,就是指数组中元素的个数
利用@或*,可以将数组扩展成列表,然后使用#来获取数组元素的个数:
${#array_name[@]}
${#array_name[*]}
其中array_name表示数组名,两种形式是等价的,选择其一即可。
如果某个元素是字符串,还可以通过指定下标的方式获取该元素的长度:
${#arr[2]}
获取arr数组第3个元素的长度(假定它是字符串)
shell获取字符串的长度起始和获取数组的长度如出一撤:
${#string_name}

//An highlighted block
  1 #!/bin/bash
  2 
  3 num=(1 2 3 4 5 6 7 8 9)
  4 echo "数组的长度为:${#num[@]}"
  5 
  6 #像数组中添加元素
  7 num[20]="www.baidu.com"
  8 echo "数组的长度为:${#num[*]}"
  9 echo "字符串的长度为:${#num[20]}"
 10 
 11 #删除数组的元素
 12 unset num[0]
 13 echo ${num[@]}

在这里插入图片描述shell数组拼接,shell数组合并
所谓shell数组拼接(数组合并),就是讲两个数组连接成一个数组。
连接数组的思路是:先利用@或*,将数扩展成列表,然后再合并到一起:
array_new=(${array1[@]} ${array2[*]})
其中array_new是拼接形成后的新数组,array1和array2是需要拼接的数组

//An highlighted block
  1 #!/bin/bash
  2 
  3 arr1=(1 2 3 4 5)
  4 arr2=(10 "www.baidu.com")
  5 arr_new=(${arr1[@]} ${arr2[*]})
  6 echo ${arr_new[@]}

在这里插入图片描述shell数组删除元素
在shell中,使用关键字unset来删除数组元素:
unset array_name[index]
其中array_name是数组名,index是数组元素的下标。
如果不写下标:
unset array_name
那么就是删除整个数组,所有元素都会消失。

//An highlighted block
  1 #!/bin/bash
  2 
  3 arr=(1 2 3 4 5 "www.baidu.com")
  4 echo ${arr[@]}
  5 
  6 unset arr[4]
  7 echo ${arr[@]}
  8 
  9 unset arr
 10 echo ${arr[*]}

在这里插入图片描述shell内建命令
所谓shell内建命令,就是有bash自身提供的命令,而不是文件系统中的某个可执行文件。
例如:用于进入或者切换目录的cd命令,虽然我们一直在使用它,但如果不加以注意很难意识到它与普通命令的性质是不一样的:该命令并不是某个外部文件,只要在shell中你就一定可以运行这个命令。
可以使用type来确定一个命令是否是内建命令:
在这里插入图片描述由此可见,cd是一个shell内建命令,而ifconfig是一个外部文件,它的位置是/sbin/ifconfig.
$PATH变量包含的目录中几乎聚集了系统中绝大多数的可执行命令,它们都是外部命令。
通常来说,内建命令会比外部命令执行的更快,执行外部命令时不但会触发磁盘I/O,还需要fork出一个单独的进程来执行,执行完成后再退出。而执行内建命令相当于调用当前shell进程的一个函数。
下面是bash shell中可直接使用的内建命令
: 扩展参数列表,执行重定向操作
. 读取并执行指定文件中的命令(在当前 shell 环境中)
alias 为指定命令定义一个别名
bg 将作业以后台模式运行
bind 将键盘序列绑定到一个 readline 函数或宏
break 退出 for、while、select 或 until 循环
builtin 执行指定的 shell 内建命令
caller 返回活动子函数调用的上下文
cd 将当前目录切换为指定的目录
command 执行指定的命令,无需进行通常的 shell 查找
compgen 为指定单词生成可能的补全匹配
complete 显示指定的单词是如何补全的
compopt 修改指定单词的补全选项
continue 继续执行 for、while、select 或 until 循环的下一次迭代
declare 声明一个变量或变量类型。
dirs 显示当前存储目录的列表
disown 从进程作业表中刪除指定的作业
echo 将指定字符串输出到 STDOUT
enable 启用或禁用指定的内建shell命令
eval 将指定的参数拼接成一个命令,然后执行该命令
exec 用指定命令替换 shell 进程
exit 强制 shell 以指定的退出状态码退出
export 设置子 shell 进程可用的变量
fc 从历史记录中选择命令列表
fg 将作业以前台模式运行
getopts 分析指定的位置参数
hash 查找并记住指定命令的全路径名
help 显示帮助文件
history 显示命令历史记录
jobs 列出活动作业
kill 向指定的进程 ID(PID) 发送一个系统信号
let 计算一个数学表达式中的每个参数
local 在函数中创建一个作用域受限的变量
logout 退出登录 shell
mapfile 从 STDIN 读取数据行,并将其加入索引数组
popd 从目录栈中删除记录
printf 使用格式化字符串显示文本
pushd 向目录栈添加一个目录
pwd 显示当前工作目录的路径名
read 从 STDIN 读取一行数据并将其赋给一个变量
readarray 从 STDIN 读取数据行并将其放入索引数组
readonly 从 STDIN 读取一行数据并将其赋给一个不可修改的变量
return 强制函数以某个值退出,这个值可以被调用脚本提取
set 设置并显示环境变量的值和 shell 属性
shift 将位置参数依次向下降一个位置
shopt 打开/关闭控制 shell 可选行为的变量值
source 读取并执行指定文件中的命令(在当前 shell 环境中)
suspend 暂停 Shell 的执行,直到收到一个 SIGCONT 信号
test 基于指定条件返回退出状态码 0 或 1
times 显示累计的用户和系统时间
trap 如果收到了指定的系统信号,执行指定的命令
type 显示指定的单词如果作为命令将会如何被解释
typeset 声明一个变量或变量类型。
ulimit 为系统用户设置指定的资源的上限
umask 为新建的文件和目录设置默认权限
unalias 刪除指定的别名
unset 刪除指定的环境变量或 shell 属性
wait 等待指定的进程完成,并返回退出状态码

1.alias用来给命令创建一个别名,若直接输入该命令且不带任何参数,则列出当前shell进程中使用了那些别名,例如ll和ls-l的效果一样。
alias new_name=‘command’
例如:
alias myShutDown=‘shutdown -h now’
别名只是临时的,只对当前shell进程有效,在子进程和其他进程中都不能使用,要想让别名对所有的shell进程都有效,就得把别名写入shell配置文件。
使用unalias内建命令可以删除当前shell进程中的别名。unlias有两种使用方法:
第一种用法是在命令后面跟上某个命令的别名,用于删除指定的别名。
第二种用法是在命令后接-a参数,删除当前shell进程中所有的别名
同样这两种方法只对当前shell进程有效,要想永久删除配置文件中定义的别名,只能进入该文件手动删除。
2.shell echo命令:输出字符串
echo是一个shell内建命令,用来在终端输出字符串,并在最后默认加上换行符。
echo 命令输出结束后默认会换行,如果不希望换行,可以加上-n参数。
默认情况下,echo不会解析以反斜杠\开头的转义字符,比如\n表示换行,echo会默认把它当做普通字符对待,原样输出,我们可以添加-e参数来让echo命令解析转义字符
echo -e “hello \nworld”

\c转义字符
有了-e参数,我们也可以使用转义字符\c来强制echo命令不换行了:echo -e “${num1} ${num2}\c”

3.read是shell内置命令,用来从标准输入中读取数据并赋值给变量。如果没有进行重定向,默认就是从键盘读取用户输入的数据;如果进行了重定向,那么可以从文件中读取数据。
read [-options] [variables]
options表示选项,variables表示用来存储数据的变量,可以有一个,也可以有多个
options和variables都是可选的,如果没有提供变量名,那么读取的数据将存放到环境变量REPLY中。
在这里插入图片描述

//An highlighted block
  1 #!/bin/bash
  2 
  3 read -p "enter some information >" name url age
  4 echo "name:$name"
  5 echo "url:$url"
  6 echo "age:$age"

在这里插入图片描述注意,必须在一行内输入所有的值,不能换行,否则只会给第一个变量赋值,后续变量都会赋值失败。本例还使用了-p选项,该选项会用一段文本来提示用户输入。

//An highlighted block
  1 #!/bin/bash
  2 
  3 read -n 1 -p "Enter a char >" char
  4 printf "\n"  #换行
  5 echo $char

在这里插入图片描述-n 1 表示只读取一个字符,运行脚本后,只要用户输入一个字符,立即读取结束,不用等待用户按下回车键。
printf “\n” 语句用来达到换行的效果,否则echo的输出结果会和用户输入。

//An highlighted block
  1 #!/bin/bash
  2 
  3 if
  4     read -t 20 -s  -p "enter password in 20 seconds(once) >" pass1 && printf "\    n" &&
  5 #第一次输入密码
  6     read -t 20 -s  -p "enter password in 20 seconds(again) >" pass2 && printf "    \n" &&
  7 #第二次输入密码
  8 [ $pass1 == $pass2 ]
  9 then
 10     echo "valid password"
 11 else
 12     echo "invalid password"
 13 fi

在这里插入图片描述这段代码中,使用了&&组合多个命令,这些命令会依次执行,并且从整体上作为if语句的条件判断条件,只要其中一个命令执行失败(退出状态为非0值),整个判断条件就失败,后续的命令也没有必要执行了。

exit:退出当前进程
exit是一个shell内置命令,用来退出当前shell进程,并返回一个退出状态,使用$?可以接受这个退出状态。
exit命令可以接受一个整数值作为参数,代表退出状态,如果不指定,默认状态值是0.
一般情况下,退出状态为0表示退出成功,退出状态为非0表示执行失败(出错)了。
exit退出状态只能是一个介于0~255之间的整数,其中只有0表示成功,其他值都表示失败。
shell进程执行出错时,可以根据退出状态来判断具体出现了什么错误,比如打开一个文件时,我们可以指定1表示文件不存在,2表示文件没有读取权限,3表示文件类型不对。

//An highlighted block
  1 #!/bin/bash
  2 
  3 echo "before exit"
  4 exit 8
  5 echo "after exit"

在这里插入图片描述可以看到,after exit并没有输出,这说明遇到exit命令后,文件就执行结束。
注意,exit表示退出当前shell进程,我们必须在新的进程中运行exit.sh,否则当前shell会话(终端窗口)会被关闭,我们就无法看到输出结果了。
接着我们使用$?来获取上一个进程的退出状态。
echo $?
可以看到是8

设置变量属性
declare和typeset都是shell内建命令,它们的用法相同,都用来设置变量的属性,不过typeset已经被弃用了,建议使用declare代替。
declare命令的用法如下:
declare [+/-] [aAfFgilprtux] [变量名=变量值]
其中,-表示设置属性,+表示取消属性,aAfFgilprtux都是具体的选项,它们的含义如下所示:
在这里插入图片描述1)

//An highlighted block
  1 #!/bin/bash
  2 
  3 declare -i m n sum     #同时将多个变量声明为整数
  4 m=10
  5 n=10
  6 sum=$m+$n
  7 echo $sum

在这里插入图片描述注意+(加号)两边没有空格

2)将变量定义为只读变量
在这里插入图片描述
3)显示变量的属性和值

在这里插入图片描述shell数学计算
如果要执行算术运算(数学计算),就离不开各种运算符号,和其他编程语言类似,shell也有很多算术运算符:

在这里插入图片描述但是shell和其他编程语言不同,shell不能直接进行算数运算,必须使用数学计算命令
在这里插入图片描述从上面的运算结果看出,默认情况下,shell不会直接进行算术运算,而是把+两边的数据(数值或者变量)当做字符串,把+当做字符串连接符,最终结果就是把两个字符串拼接在一起形成一个新的字符串。
这是因为,在bash shell中,如果不特别指明,每一个变量的值都是字符串,无论你给变量赋值时有没有使用引号,值都会以字符串的形式存储。
换句话说,bash shell在默认情况下不会区别变量的类型,即使你将整数和小数赋值给变量,它们也会被视为字符串,这一点和大部分的编程语言不同。
数学计算命令
要想让数学计算发挥作用,必须使用数学计算命令,shell中常用的数学计算命令如下表所示:
在这里插入图片描述shell(())的用法
双小括号(())是bash shell中专门用来进行整数运算的命令,它的效率很高,写法灵活,是企业运维中常用的运算命令。
注意:(())只能进行整数运算,不能对小数(浮点数)或者字符串进行运算,后续讲到的bc命令可以用于小数运算。
双小括号(())的语法格式为:
((表达式))
通俗地将,就是讲数学运算表达式放在((和))之间。
表达式可以只有一个,也可以有多个,多个表达式之间以逗号,分隔,对于多个表达式的情况,以最后一个表达式的值作为整个(())命令的执行结果。
可以用 获取(())命令的执行结果,这和使用 获取(())命令的执行结果,这和使用 获取(())命令的执行结果,这和使用获得变量值是类似的。
在这里插入图片描述在(())中使用变量无需加上$前缀,(())会自动解析变量名,这使得代码更加简洁,也符合程序员的书写习惯。
1)利用(())进行简单的数值计算。
在这里插入图片描述2)利用(())进行稍微复杂一些的综合算术运算。
在这里插入图片描述3)利用(())进行逻辑运算
在这里插入图片描述最后一句是一个简单的if语句的格式。

4)利用(())进行自增(++)和自减(- -)运算
在这里插入图片描述上面的例子可以看出来,shell自增和自减的结果跟其他的编程语言一样,前置则先自增或者自减再执行表达式,后置则先执行表达式,再自增或者自减。

5)利用(())同时对多个表达式进行计算
在这里插入图片描述第一个先计算第一个表达式,再计算第二个表达式;第二个以最后一个表达式作为整个(())命令的执行结果

shell let命令
let命令和双小括号(())的用法是类似的,它们都是用来对整数进行运算,和(())一样,let命令也只能进行整数运算,不能进行小数(浮点数)或者字符串进行运算。
shell let命令的语法为:
let 表达式
let ‘表达式’
let “表达式”
它们都等价于((表达式))
当表达式中含有shell特殊字符(例如|)时,需要用双引号""或者单引号’'将表达式包围起来。
和(())类似,let也支持一次性计算多个表达式,并以最后一个表达式的值作为整个let命令的执行结果,但是对于多个表达式之间的分隔符,let和(())是有区别的:
let命令以空格来分隔多个表达式;
(())以逗号,来分隔多个表达式。
另外还要注意,对于类似 let x+y这样的写法,shell虽然计算了x+y的值,但是却将结果丢弃;若不想这样,可以使用let sum=x+y将x+y的结果保存在变量sum中。
在这里插入图片描述1)给变量加数值
在这里插入图片描述2)let后面可以跟多个表达式
在这里插入图片描述shell if else语句
和其他编程语言类似,shell也支持选择结构,并且有两种形式,分别是if else语句和case in语句。

if语句
最简单的用法就是只用if语句:
if condition
then
statement
fi
condition是判断条件,如果condition成立(返回真),那么then后边的语句将会被执行,如果condition不成立(返回假),那么不执行任何语句。
注意必须以fi来闭合,fi倒过来写就是if,也正是有了fi来结尾,所以即使有多条语句也不需要用{}包围起来。
也可以将then if写在一行:
if condition;then
statement
fi
注意condition后边的分号;,当if和then位于同一行的时候,这个分号是必须的,否则会有语法的错误。
1)使用if来比较两个数字的大小

//An highlighted block
  1 #!/bin/bash
  2 
  3 read a
  4 read b
  5 
  6 if(( $a == $b ))
  7 then
  8     echo "a和b相等"
  9 fi

在这里插入图片描述(())是一种数学计算命令,它除了可以进行最基本的加减乘除运算,还可以进行大于、小于、等于等关系运算,以及与、或、非逻辑运算。当a和b相等时,(( a = = a == a==b))判断条件成立,进入if,执行then后边的echo的语句。
2)在判断条件中也可以使用逻辑运算符

//An highlighted block
  1 #!/bin/bash
  2 
  3 read age
  4 read iq
  5 
  6 if (( $age > 18 && iq < 60))
  7 then
  8     echo "成年了智商还不合格"
  9     echo "来学习编程吧,能迅速让你的智商提高"
 10 fi

在这里插入图片描述if else 语句
如果有两个分支,就可以使用if else语句,它的格式为:
if condition
then
statement1
else
stetement2
fi

如果condition成立,那么then后边的stetement1语句将会被执行,否则,执行else后边的statement2语句。

//An highlighted block
  1 #!/bin/bash
  2 
  3 read a
  4 read b
  5 
  6 if (( a==b ))
  7 then
  8     echo "a和b相等"
  9 else
 10     echo "a和b不相等"
 11 fi

在这里插入图片描述if elif else 语句
shell支持任意数目的分支,当分支比较多时,可以使用 if elif else结构,它的格式为:
if condition1
then
statement1
elif condition2
then
statement2
elif condition3
then
statement3

else
statementn
fi
注意,if和elif后边都跟着then
整条语句的执行逻辑为:
如果condition1成立,那么就执行if后边的statement1;如果不成立,那么继续执行elif,判断condition2.
如果condition2成立,那么就执行elif后边的statement2;如果不成立,那么继续执行elif,判断condition3.
如果condition3成立,那么就执行elif后边的statement3;如果不成立,那么继续执行elif,判断condition4.
如果所有的if和elif判断都不成立,就进入最后的else,执行statementn。

//An highlighted block
3 read age
  4 
  5 if (( $age <= 2))
  6 then
  7     echo "婴儿"
  8 elif (( $age >= 3 && $age <= 18))
  9 then
 10     echo "骚年"
 11 elif (( $age <= 60))
 12 then
 13     echo "青壮年"
 14 else
 15     echo "老年"
 16 fi

在这里插入图片描述再来一个,输入一个整数,输出该整数对应的星期几的英文表示:

//An highlighted block
  1 #!/bin/bash
  2 
  3 printf "Input integer number:"
  4 read num
  5 
  6 if (( num==1 ));then
  7     echo "Monday"
  8 elif (( num==2 ));then
  9     echo "Tuesday"
 10 elif (( num==3 ));then
 11     echo "Wednesday"
 12 elif (( num==4 ));then
 13     echo "Thursday"
 14 elif (( num==5 ));then
 15     echo "Friday"
 16 elif (( num==6 ));then
 17     echo "Saturday"
 18 elif (( num==7 ));then
 19     echo "Sunday"
 20 else
 21     echo "eror"
 22 fi

在这里插入图片描述shell退出状态
每一条shell命令,不管是bash内置命令(cd,echo),还是外部的Linux命令(ls,awk),还是自定义的shell函数,当它退出(运行结束)时,都会返回一个比较小的整数值给调用(使用)它的程序,这就是命令的退出状态(exit statu)。
很多Linux命令其实就是一个C语言程序,main()函数的最后都有一个return 0,如果程序想在中间退出,还可以使用exit 0,这其实就是C语言程序的退出状态,当有其它程序调用这个程序时,就可以捕获这个退出状态。
if语句的条件判断,从本质上讲,判断的就是命令的退出状态。
按照惯例来说,退出状态为0表示成功,也就是说,程序执行完成并且没有遇到任何问题,除0以外的其他任何退出状态都为失败。
之所以说这是惯例而非规定,因为也有例外,比如diff命令用来比较两个文件的不同,对于没有差别的文件返回0,对于找到差别的文件返回1,对于无效文件名返回2.
在shell中我们可以使用$?查看命令的退出状态:
在这里插入图片描述退出状态和逻辑运算符的组合
shell if语句的一个神奇之处就是允许我们使用逻辑运算符将多个退出状态组合起来,这样就可以一次判断多个条件了。
在这里插入图片描述实例,将用户输入的内容写入文件中

//An highlighted block
  1 #!/bin/bash
  2 
  3 read filename
  4 read text
  5 
  6 if test -w $filename && test -n $text
  7 then
  8     echo $text > $filename
  9     echo "写入成功"
 10 else
 11     echo "写入失败"
 12 fi

在这里插入图片描述test是shell内置命令,可以对文件或者字符串进行检测,其中,-w选项用来检测文件是否存在并且可写,-n选项用来检测字符串是否为空。
>表示重定向,默认情况下,echo向控制台输出,这里我们将输出结果重定向到文件。

shell test命令
test是shell内置命令,用来检测某个条件是否成立,test通常和if语句一起使用,并且大部分if语句都依赖test。
test命令有很多选项,可以进行数值、字符串和文件三个方面的检测。
shell test命令的用法为:
test expression
当test判断expression成立时,退出状态为0,否则为非0.
test命令也可以简写为[],它的用法为:
[ expression ]
注意[]和expression之间的空格,这两个空格是必须的,否则会导致语法错误。[]的写法更加简洁,比test使用频率高。
在if else中,我们使用(())进行数值比较,接下来看一下如何使用test命令进行数值比较。

//An highlighted block
  1 #!/bin/bash
  2 
  3 read age
  4 
  5 if test $age -le 2;then
  6     echo "婴儿"
  7 elif test $age -ge 3 && test $age -le 8;then
  8     echo "幼儿"
  9 elif [ $age -ge 9 ] && [ $age -le 17 ];then
 10     echo "少年"
 11 elif [ $age -ge 18 ] && [ $age -le 25 ];then
 12     echo "成年"
 13 elif [ $age -ge 26 ] && [ $age -le 40 ];then
 14     echo "青年"
 15 elif test $age -ge 41 && [ $age -le 60 ];then
 16     echo "中年"
 17 else
 18     echo "老年"
 19 fi

在这里插入图片描述其中,le-选项表示小于等于,-ge选项表示大于等于,&&是逻辑与运算符。

1)与文件检测相关的test选项
在这里插入图片描述在这里插入图片描述shell test文件检测:

//An highlighted block
  1 #!/bin/bash
  2 
  3 read filename
  4 read text
  5 
  6 if test -w $filename && test -n $text
  7 then
  8     echo $text > $filename
  9     echo "写入成功"
 10 else
 11     echo "写入失败"
 12 fi

在这里插入图片描述与数值比较相关的test选项
在这里插入图片描述注意:test只能用来比较整数,小数相关的比较还得依赖bc命令。

//An highlighted block
  1 #!/bin/bash
  2 
  3 read a b
  4 
  5 if test $a -eq $b
  6 then
  7     echo "两个数字相等"
  8 else
  9     echo "两个数不相等"
 10 fi

在这里插入图片描述与字符串判断相关的test选项
在这里插入图片描述在shell中,== 、> 、<,不能比较数字,只能用来比较字符串。
其次不管是比较数字还是字符串,shell都不支持>= 、<=运算符,切记。

//An highlighted block
  1 #!/bin/bash
  2 
  3 read str1
  4 read str2
  5 
  6 #检测字符串是否为空
  7 if [ -z "$str1" ] || [ -z "$str2" ]
  8 then
  9     echo "字符串不能为空"
 10     exit 0
 11 fi
 12 
 13 #比较字符串
 14 if [ $str1 = $str2 ]
 15 then
 16     echo "两个字符串相等"
 17 else
 18     echo "两个字符串不相等"
 19 fi

在这里插入图片描述与逻辑运算相关的test选项
在这里插入图片描述

//An highlighted block
  1 #!/bin/bash
  2 
  3 read str1
  4 read str2
  5 
  6 #检测字符是否为空
  7 if [ -z "$str1" -o -z "$str2" ]         #使用-o选项取代之前的||
  8 then
  9     echo "字符串不能为空"
 10     exit 0
 11 fi
 12 
 13 #比较字符串
 14 if [ $str1 = $str2 ]
 15 then
 16     echo "两个字符串相等"
 17 else
 18     echo "两个字符串不相等"
 19 fi

在这里插入图片描述前面使用两个[]命令,并使用||运算符将它们连接起来,这里使用-o选项,只使用一个[]命令就可以了。

在test中使用变量建议用双引号包围起来
test和[]都是命令,一个命令本质上对应一个程序或者函数。即使是一个程序,它们也有入口函数,例如C语言程序的入口是main(),运行C语言程序就从main()函数开始,所以也可以将一个程序等效为一个函数,这样我们就不用再区分函数和程序了,直接将一个命令和一个函数对应起来即可。
有了以上认知,就很容易看透命令的本质了:使用一个命令其实就是调用一个函数,命令后面附带的选项和参数最终都会作为实参传递给函数。
假设test命令对应的函数时func(),使用test -z $str1命令时,会将变量$str1替换成字符串:
1.如果$str1是一个正常的字符串,比如abc123,那么替换后的效果就是test -z abc123,调用func()函数的形式就是func(“-z abc123”)。test命令后面附带的所有选项和参数会被看成一个整体,并作为实参传递进函数。
2.如果$str1是一个空字符串,那么替换后的效果就是test -z,调用
function()函数的形式就是fun(“-z”),这就比较奇怪了,因为-z选项没有和参数成对出现,func()在分析时就会出错。

如果我们给 s t r 1 变量加上双引号,当 str1变量加上双引号,当 str1变量加上双引号,当str1是空字符串时,test -z"$str1"
就会被替换为test -z"“,调用fanc()函数的形式就是func(”-z"“”),很显然,-z选项后面跟的是一个空字符串(\表示转义字符),这样func()在分析时就不会出错了。
所以当在test命令中使用变量时,强烈建议将变量用双引号""包围起来,这样能避免变量为空值是导致的很多奇葩问题。

总结
test命令比较奇葩,<、>、==只能用来比较字符串,不能用来比较数字,比较数字需要使用-eq、-gt等选项;不管是比较字符串还是数字,test都不支持>=、<=。
对于整型数字的比较,建议使用(())。

shell[[ ]]
[[ ]]是shell内置关键字,它和test命令类似,也用来检测某个条件是否成立。
test能做到的,[[ ]]也能做到,而且[[ ]]做的更好,test做不到的,[[ ]]还能做到,可以认为[[ ]]是test的升级版,对细节进行了优化,并且扩展了一些功能。
[[ ]]的用法为:
[[ expression ]]
当[[ ]]判断expression成立时,退出状态为0,否则为非0值。注意[[ ]]和expression之间的空格,这两个空格是必须的,否则会导致语法错误。
[[ ]]不需要注意某些细末枝节
[[ ]]是shell内置关键字,不是命令,在使用时没有给函数传递参数的过程,所以test命令的某些注意事项在[[ ]]中就不存在了,具体包括:
不需要把变量名用双引号""包围起来,即使变量时空值,也不会出错。
不需要、也不能对<、>进行转义,转义后会出错。

//An highlighted block
  1 #!/bin/bash
  2 
  3 read str1
  4 read str2
  5 
  6 if [[ -z $str1 ]] || [[ -z $str2 ]]     #不需要对变量名加双引号
  7 then
  8     echo "字符串不能为空"
  9 elif [[ $str1 < $str2 ]]
 10 then
 11     echo "str1<str2"
 12 else
 13     echo "str1=>str2"
 14 fi

在这里插入图片描述[[ ]] 支持逻辑运算符
对多个表达式进行逻辑运算时,可以使用逻辑运算符将多个test命令连接在一起。
[ -z “$str1” ] || [ -z “$str2” ]
或者
[ -z “$str1” -o -z “$str2” ]
但是这两种写法都有点别扭,完美的写法是在一个命令中使用逻辑运算符将多个表达式连接起来。在[[ ]] 中可以实现,[[ ]]支持&&、||和!三种逻辑运算符。
使用[[ ]]对上面的语句进行改进:
[[ -z $str1 || -z $str2 ]]
这个写法就比较简洁了。
注意,[[ ]]剔除了test命令的-o和-a选项,只能使用|| 和&&。这意味着不能写成下面的形式:
[[ -z $str1 -o -z $str2 ]]
当然,使用多个[[ ]]连接起来依然是可以的,因为这是shell本身提供的功能,跟[[ ]]或者test没有关系:
在这里插入图片描述
[[ ]]支持正则表达式
在shell [[ ]]中,可以使用=~来检测字符串是否符合某个正则表达式,它的用法为:
[[ str =~ regex ]]
str表示字符串,regex表示正则表达式。

下面的代码检测一个字符串是否是手机号:

//An highlighted block
  1 #!/bin/bash
  2 
  3 read tel
  4 
  5 if [[ $tel =~ ^1[0-9]{10}$ ]]
  6 then
  7     echo "你输入的是手机号码"
  8 else
  9     echo "你输入的不是手机号码"
 10 fi

在这里插入图片描述对于^1[0-9]{10}$的说明:
^匹配字符串的开头(一个位置)
[0-9]{10}匹配连续的是个数字
$匹配字符串的末尾(一个位置)
总结
有了 [[ ]]完全可以代替test或者[ ],但是[[ ]] 对数字的比较仍然不友好,建议使用if判断条件的时候,用(())来处理整型数字,用[[ ]] 来处理字符串或者文件。

shell case in
和其他编程语言一样,shell也支持两种分支结构(选择结构),分别是if else 语句和case in语句。
当分支较多,并且判断条件比较简单时,使用case in 语句就比较方便了。

//An highlighted block
  1 #!/bin/bash
  2 
  3 printf "input integer number:"
  4 read num
  5 
  6 case $num in
  7     1)
  8         echo "Monday"
  9         ;;
 10     2)
 11         echo "Tuesday"
 12         ;;
 13     3)
 14         echo "Wednesday"
 15         ;;
 16     4)
 17         echo "Thursday"
 18         ;;
 19     5)
 20         echo "Friday"
 21         ;;
 22     6)
 23         echo "Saturday"
 24         ;;
 25     7)
 26         echo "Sunday"
 27         ;;
 28     *)
 29         echo "error"
 30 esac

在这里插入图片描述case in的基本格式如下:
case expession in
pattern1)
statement1

;;
pattern2)
statement2

;;
…………
*)
statementn
esac

case 、in和esac都是shell关键字,expression表示表达式,pattern表示匹配模式。
expression既可以是一个变量、一个数字、一个字符串,还可以是一个数学表达式,或者是命令的执行结果,只要能够得到expression的值就可以。
pattern可以是一个数字、一个字符串,甚至是一个简单的正则表达式。

case会将expression的值与pattern1、pattern2、pattern3逐个进行匹配:
如果expression和某个模式(比如pattern2)匹配成功,就会执行这模式(比如pattern2)后面对应的多有语句(该语句可以有一条,也可以有多条)知道遇见双分号;;才停止;然后整个case语句就执行完了,程序会跳出整个case语句,执行esac后面的其他语句。
如果expression没有匹配到任何一个模式,那么就执行*)后面的语句(*表示其他所有值),知道遇见双分号;;或者esac才结束。*)相当于多个if分支语句最后的else部分。
如果你有其他语言的编程经验,这里的;;和*)就相当于其他编程语言的break和default。
对*)的几点说明:
shell case in语句中的*)用来“托底”,万一expression没有匹配到任何一个模式,*)部分可以做一些善后工作,或者给用户一些提示。
可以没有*)部分,如果expression没有匹配到任何模式,那就不执行任何操作。
除了最后一个分之外(这个分支可以是普通分支,也可以是*)分支),其它的每个分支都必须以;;结尾,;;代表一个分支的结束,不写的话会有语法错误。最后一个分支可以写;;也可以不写,因为无论如何,执行到esac都会结束整个case in语句。
上面的代码是case in最常见的用法,即expression部分是一个变量,pattern部分是一个数字或者表达式。
case in和正则表达式
case in的pattern部分支持简单的正则表达式,具体来说,可以使用一下几种格式:
在这里插入图片描述如果不加以说明,shell的值都是字符串,expression和pattern也是按照字符串的方式来匹配的;上面的代码看起来是判断数字是否相等,其实是判断字符串是否相等。
最后一个分支*)并不是什么语法规定,它只是一个正则表达式,*表示任意字符串,所以不管expression的值是什么,*)总能匹配成功。

//An highlighted block
  1 #!/bin/bash
  2 
  3 printf "Input a character:"
  4 read -n 1 char
  5 
  6 case $char in
  7     [a-zA-Z])
  8         printf "\nletter\n"
  9         ;;
 10     [0-9])
 11         printf "\ndigit\n"
 12         ;;
 13     [,.?!])
 14         printf "\nPunctuation\n"
 15         ;;
 16     *)
 17         printf "\nerror\n"
 18 esac

在这里插入图片描述shell while循环
while循环是shell脚本中最简单的一种循环,当条件满足时,while重复的执行一组语句,当条件不满足时,就退出while循环。
shell while循环用法如下:
while condition
do
statements
done

condition表示条件判断,statements表示要执行的语句,可以只有一条,也可以有多条,do和done都是shell中的关键字。

while循环的执行流程为:
1.先对condition进行判断,如果该条件成立,就进入循环,执行while循环体中的额语句,也就是do和done之间的语句,这样就完成了一次循环。
2.每一次执行到done额时候都会重新判断condition是否成立,如果成立,就进入下一次循环,继续执行do和done之间的语句;如果不成立,就结束整个while循环,执行done后面的其他shell代码。
3.如果一开始condition就不成立,那么程序就不会进入循环体,do和done之间的语句就没有执行的机会。
注意:在while循环体中必须有相应的语句使得condition越来越趋近于“不成立”,只有这样才能最终退出循环,否则while就成了死循环,会一直执行下去,永无休止。

while语句和if else语句中的condition用法都是一样的,你可以使用test或者[ ],也可以使用(())或者[[ ]]。
while循环举例:
1)计算从1加到100的和

//An highlighted block
  1 #!/bin/bash
  2 
  3 i=1
  4 sum=0
  5 
  6 while ((i <= 100))
  7 do
  8     ((sum += i))
  9     ((i++))
 10 done
 11 echo "The sum is:$sum"

在这里插入图片描述对上面的例子进行改进,计算从m加到n的值

//An highlighted block
  1 #!/bin/bash
  2 
  3 read m
  4 read n
  5 sum=0
  6 
  7 while (( m<=n))
  8 do
  9     ((sum += m))
 10     ((m++))
 11 done
 12 echo "The sum is:$sum"

在这里插入图片描述2)实现一个简单的加法计算器,用户每行数一个数字,计算所有数字的和。

//An highlighted block
  1 #!/bin/bash
  2 
  3 sum=0
  4 
  5 echo "请输入你要计算的数字,按Ctrl+D组合键结束读取"
  6 while read n
  7 do
  8     (( sum+=n))
  9 done
 10 
 11 echo "The sum is:$sum"

在这里插入图片描述在终端读取数据,可以等价为在文件中读取数据,按下Ctrl+D组合键表示读取到文件流的末尾,此时read就会读取失败,得到下一个非0值得退出状态,从而导致判断条件不成立,结束循环。

shell until循环
until循环和while循环恰好相反,当判断条件不成立时才能进行循环,一旦判断条件成立,就会终止循环。
until的使用场景很少,一般使用while即可。
shell until循环的用如下:
until condition
do
statements
done
condition表示条件判断,statements表示要执行的语句(可以只有一条,也可以有多条),do和done都是shell中的关键字。

until循环的额执行流程:
1.先对condition进行判断,如果该条件不成立,就进入循环,执行until循环体重的语句(do和done之间的语句),这样就完成一次循环。
2.每一次执行到done的时候都会重新判断condition是否成立,如果不成立,就进入下一次循环,继续执行循环体中的代码,如果成立,就结束整个until循环,执行done后面的额shell代码。
3.如果一开始condition成立,那么程序就不会进入循环体,do和done之间的语句就没有执行的机会。

注意:在until 循环体中必须有相应的语句使得condition越来越趋近于“成立”,只有这样才能最终退出循环,否则until就成了死循环,会一直执行下去,永无休止。
1)从1加到100的和

//An highlighted block
  1 #!/bin/bash
  2 
  3 i=1
  4 sum=0
  5 
  6 until ((i > 100))
  7 do
  8     ((sum+=i))
  9     ((i++))
 10 done
 11 echo "the sum is:$sum"

在这里插入图片描述shell for循环和for int循环
除了while循环和until循环,shell脚本还提供了for循环,它更加灵活易用,更加简洁明了,shell for循环有两种使用形式。
C语言风格的for循环
for((exp1;exp2;exp3))
do
statements
done
几点说明:
exp1、exp2、exp3是三个表达式,其中exp2是判断条件,for循环根据exp2的结果来决定是否继续下一次循环;
statements是循环体的语句,可以有一条或者多条
do和done是shell中的关键字

它的运行过程为:
1.先执行exp1
2.在执行exp2,如果它的判断结果是成立的,则执行循环体中的语句,否则结束整个for循环。
3.执行完循环体后再执行exp3.
4.重复执行步骤2.和3.,直到exp2的判断结果不成立,就结束循环。
上面的步骤中,2和3合并在一起算作一次循环,会重复执行,for语句的主要作用就是不断执行步骤2和3
exp1仅在第一次循环时执行,以后都不再执行,可以认为这是一个初始化语句。exp2一般是一个关系表达式,决定了是否还要继续下次循环,称为“循环条件”。exp3很多情况下是一个带有自增或自减运算的表达式,以使循环条件逐渐变得“不成立”

//An highlight block
  1 #!/bin/bash
  2 
  3 sum=0
  4 for ((i=1;i<=100;i++))
  5 do
  6     ((sum+=i))
  7 done
  8 
  9 echo "The sum is:$sum"

在这里插入图片描述由此可以总结出for循环的一般形式为:
for ((初始化语句;判断条件;自增或自减))
do
statements
done

for循环中的三个表达式
for循环中的exp1 (初始化语句)、exp2(判断条件)、exp3(自增或自减)都是可选项,都可以省略(但是分号;必须保留)
1)修改从1加到100的和,省略exp1

//An highlighted block
  1 #!/bin/bash
  2 
  3 sum=0
  4 i=1
  5 
  6 for ((;i <=100;i++))
  7 do
  8     ((sum+=i))
  9 done
 10 
 11 echo "The sum is:$sum"

在这里插入图片描述2)省略exp2 ,就没有了判断条件,如果不作其他处理就会成为死循环,我们可以在循环体内部使用break关键字强制结束循环:

//An highlighted block
  1 #!/bin/bash
  2 
  3 sum=0
  4 for ((i=0;;i++))
  5 do
  6     if (( i>100));then
  7         break
  8     fi
  9     ((sum+=i))
 10 done
 11 
 12 echo "The sum is:$sum"

在这里插入图片描述3)省略exp3,就不会修改exp2中的变量,这是可以在循环体中加入修改变量的语句:

//An highlighted block
  1 #!/bin/bash
  2 
  3 sum=0
  4 for ((i=0;i<=100;))
  5 do
  6     ((sum+=i))
  7     ((i++))
  8 done
  9 echo "The sum is:$sum"

在这里插入图片描述4)同时省略三个表达式

//An highlighted block
  1 #!/bin/bash
  2 
  3 sum=0
  4 i=1
  5 
  6 for ((;;))
  7 do
  8     if ((i >100));then
  9         break
 10     fi
 11     ((sum+=i))
 12     ((i++))
 13 done
 14 echo "The sum is:$sum"

在这里插入图片描述python风格的for in循环
Python风格的for in循环的用法如下:
for variable in value_list
do
statements
done
variable表示变量,value_list表示取值列表,in是shell中的关键字。
每次循环都会从value_list中取出一个值赋给变量variable,然后进入循环体(do和done之间的部分),执行循环体的statements。直到取完value_list中的所有值,循环就结束了。

shell for in 循环举例:

//An highlighted block
  1 #!/bin/bash
  2 
  3 sum=0
  4 
  5 for n in 1 2 3 4 5 6 7 8 9
  6 do
  7     echo $n
  8     ((sum+=n))
  9 done
 10 
 11 echo "The sum is :$sum"

在这里插入图片描述对于value_list的说明
取值列表value_list的形式有多种,可以直接给出具体的值,也可以给出一个范围,还可以使用命令产生的结果,甚至使用通配符。
1)直接给出具体的值
可以在关键字in后面直接给出具体的值,多个值以空格分隔,比如:
1 2 3 4 5、“abc” “888” "jack"等

上面的代码中用一组数字作为取值列表,下面演示一下用一组字符串作为取值:

//An highlighted block
  1 #!/bin/bash
  2 
  3 for str in "www.baidu.com" "你好呀" "888" "tom and jerry"
  4 do
  5     echo $str
  6 done

在这里插入图片描述2)给出一个取值范围
给出一个取值范围的具体格式为:
{start…end}
start表示起始值,end表示终止值,注意中间用两个点号相连,而不是三个。这种形式只支持数字和字母
例如:计算从1到100的和

//An highlighted block
  1 #!/bin/bash
  2 
  3 sum=0
  4 for i in {1..100}
  5 do
  6     ((sum+=i))
  7 done
  8 echo "The sum is:$sum"

在这里插入图片描述再如,输出A到z之间的所有字符:

//An highlighted block
  1 #!/bin/bash
  2 
  3 for i in {A..z}
  4 do
  5     printf "%c " $i
  6 done

在这里插入图片描述可以发现,shell是根据ASCII码表来输出的。
3)使用命令的执行的结果
使用反引号’'或者$()都可以取得命令的执行结果
例如,计算从1到100之间所有偶数的和:

//An highlighted block
  2 
  3 sum=0
  4 
  5 for i in $(seq 2 2 100)
  6 do
  7     ((sum+=i))
  8 done
  9 echo $sum

在这里插入图片描述seq是一个Linux命令,用来生产某个范围内的整数,并且可以设置步长。seq 2 2 100表示从2开始,每次增加2,到100结束

再如,列出当前目录下所有的shell脚本文件:

//An highlighted block
  1 #!/bin/bash
  2 
  3 for filename in $(ls *.sh)
  4 do
  5     echo $filename
  6 done

在这里插入图片描述ls是一个Linux命令,用来列出当前目录下所有的文件,*.sh表示匹配后缀名为.sh的文件,也就是shell脚本。
4)使用通配符
shell通配符可以认为是一种精简化的正则表达式,通常用来匹配目录或者文件,而不是文本。
有了shell通配符,不使用ls命令也能显示当前目录下的所有脚本文件:

//An highlighted block
  1 #!/bin/bash
  2 
  3 for filename in *.sh
  4 do
  5     echo $filename
  6 done

在这里插入图片描述5)使用特殊变量
shell中有多个特殊变量,例如$#、$*、$@、$?、$$等,在value_list中使用它们。

//An highlighted block
  1 #!/bin/bash
  2 
  3 function func(){
  4     for str in $@
  5     do
  6         echo $str
  7     done
  8 }
  9 
 10 func c++ java python

在这里插入图片描述我们也可以省略value_list,省略的效果和使用$@一样。

//An highlighted block
  1 #!/bin/bash
  2 
  3 function func(){
  4     for str
  5     do
  6         echo $str
  7     done
  8 }
  9 
 10 func c++ java python

在这里插入图片描述shell select in 循环
select in循环用来增强交互性,它可以显示出带编号的菜单,用户输入不同的功能。
select in是shell独有的一种循环,非常适合终端(Terminal)这样的交互场景。
shell select in循环的用法如下:
select variable in value_list
do
statements
done
variable 表示变量,value_list表示取值列表,in是shell中的关键字,你看,select in和for in的语法是多么的相似。

//An highlighted block
  1 #!/bin/bash
  2 
  3 echo "What is your favourite OS?"
  4 select name in "Linux" "Windows" "Mac OS" "UNIX" "Android"
  5 do
  6     echo $name
  7     echo "You have selected $name"
  8 done

在这里插入图片描述#?用来提示用户输入菜单编号;^D表示按下Ctrl+D组合键,它的作用是结束select in循环。
运行到select语句后,取值列表value_list中的内容会以菜单的形式显示出来,用户输入菜单编号,就表示选中了某个值,这个值就会赋给变量variable,然后再执行循环体中的statements(do 和done之间的部分)。
每次循环时select都会要求用户输入菜单编号,并使用环境变量ps3的值作为提示符,ps3的默认值为#?,修改ps3的值就可以修改提示符。
如果用户输入的菜单编号不在范围之内,就会给variable赋一个空值,如果用户输入一个空值(什么也不输入,直接回车),会重新显示一遍菜单。
注意select 是无限循环(死循环),输入空值,或者输入的值无效,都不会结束循环,只有遇到break语句,或者按下Ctrl+D组合键才能结束循环。
select in通常和case in一起使用,在用户输入不同的编号时可以做出不同的反应。

//An highlighted block
  1 #!/bin/bash
  2 
  3 echo "What is your favourite OS?"
  4 select name in "Linux" "Windows" "Mac OS" "UNIX" "Android"
  5 do
  6     case $name in
  7         "Linux")
  8         echo "Linux是一个类UNIX操作系统"
  9         break
 10         ;;
 11         "Windows")
 12         echo "windows是微软开发的个人电脑操作系统"
 13         break
 14         ;;
 15         "Mac OS")
 16         echo "mac os是苹果公司基于UNIX开发的一款图形界面操作系统"
 17         break
 18         ;;
 19         "UNIX")
 20         echo "UNIX是操作系统的开山鼻祖,现在已经逐渐退出历史舞台"
 21         break
 22         ;;
 23         "Andriod")
 24         echo "Andriod是由Google开发的手机操作系统"
 25         break
 26         ;;
 27         *)
 28         echo "输入错误,请重新输入"
 29         esac
 30 done

在这里插入图片描述用户只要输入正确的编号才会结束循环,如果输入错误,会被要求重新输入。

shell break和continue跳出循环
使用while、until、for、select循环时,如果想提前结束循环(在不满足结束条件的情况下结束循环),可以使用break或者continue关键字。
在shell中的break和continue能够跳出多层循环,也就是说,内层循环中的break和continue能够跳出外层循环,
break关键字
shell break关键字的用法为:
break n
n表示跳出循环的层数,如果省略n,则表示跳出当前整个循环。break关键字通常和if语句一起使用,即满足条件时便跳出循环。

//An highlighted block
  1 #!/bin/bash
  2 
  3 sum=0
  4 
  5 while read n;do
  6     if ((n>0));then
  7         ((sum+=n))
  8     else
  9         break
 10     fi
 11 done
 12 
 13 echo "sum=$sum"

在这里插入图片描述while循环通过read命令的退出状态来判断循环条件是否成立,只有当按下Ctrl+D组合键(表示输入结束)时,read n才会判断失效,此时while循环终止。
除了按下Ctrl+D组合键,你还可以输入一个小于等于0的整数,这样会执行break语句来终止循环(跳出循环)。

使用break跳出双层循环:

//An highlighted block
  1 #!/bin/bash
  2 i=0
  3 while ((++i));do
  4     if ((i>4));then
  5         break
  6     fi  
  7 
  8     j=0
  9     while ((++j));do
 10         if ((j>4));then
 11             break
 12         fi
 13         printf "%-4d" $((i*j))
 14     done
 15 
 16     printf "\n"
 17 done

在这里插入图片描述当j>4成立时,执行第二个break,跳出内层循环;外层循环依然执行,直到i>4成立,跳出外层循环。内层循环共执行4次,外层循环执行1次。
也可以在break后面跟一个数字,让他一次性跳出两层循环:

//An highlighted block
  1 #!/bin/bash
  2 
  3 i=0
  4 while ((++i));do
  5     j=0
  6     while ((++j));do
  7         if ((i>4));then
  8             break 2
  9         fi
 10         if ((j>4));then
 11             break
 12         fi
 13         printf "%-4d" $((i*j))
 14     done
 15     printf "\n"
 16 done

在这里插入图片描述修改代码后,所有的break都移到内层循环了,break 2这条语句,它使得程序可以一次性跳出两层循环,也就是先跳出内层循环,再跳出外层循环。

continue关键字
shell continue关键字的用法为:
continue n
n表示循环的层数:
如果省略n,则表示continue只对当前层次的循环语句有效,遇到continue会跳过本次循环,忽略本次循环的剩余代码,直接进入下一次循环。
如果带上n,比如n的值为2,那么continue对内层和外层循环语句都有效,不但内层会跳过本次循环,外层也会跳过本次循环,其效果相当于内层循环和外层循环同时执行了不带n的continue。

//An highlighted block
  1 #!/bin/bash
  2 
  3 sum=0
  4 
  5 while read n;do
  6     if (( n<1 || n>100 ));then
  7         continue
  8     fi
  9     (( sum+=n ))
 10 done
 11 
 12 echo "sum = $sum"

在这里插入图片描述当if条件成立时,就执行continue语句,跳出当前循环,也就是跳过了((sum +=n))这条语句。
注意只有按下ctrl+D组合键才能结束,read n才会判断失败,while循环才会结束。
使用continue跳出多层循环:

//An highlighted block
  1 #!/bin/bash
  2 
  3 for ((i=1;i<=5;i++ ));do
  4     for ((j=1;j<=5;j++ ));do
  5         if (( i*j ==12 ));then
  6             continue 2
  7         fi
  8         printf "%d*%d = %-4d" $i $j $((i*j))
  9     done
 10     echo
 11 done

在这里插入图片描述break用来结束所有循环,循环语句不再有执行的机会;continue用来结束本次循环,直接跳到下一次循环,如果循环条件成立,还会继续循环。

shell函数
shell函数的本质是一段可以重复使用额脚本代码,这段代码被提前编写好了,放在了指定的位置,使用时直接调用即可。
shell函数定义的语法格式如下:
function name(){
statements
[return value]
}
function是shell中的关键字,专门用来定义函数。
name是函数名;
statements是函数要执行的代码,也就是一组语句;
return value表示函数的返回值,其中return是shell关键字,专门用在函数中返回一个值;这一部分可以写也可以不写。
有{}包围的部分称为函数体,调用一个函数,实际上就是执行函数体中的代码。
函数定义的简化
函数定义的时候也可以不写function关键字:
name(){
statements
[return value]
}
如果写了function关键字,也可以省略函数名后面的小括号:
function name{
statements
[return value]
}
函数调用
调用shell函数时可以给它传递参数,也可以不传递。如果不传递参数,直接给出函数名字即可:
name
如果传递参数,name多个参数之间以空格分隔:
name param1 param2 param3
不管哪种形式,函数名后面都不需要带括号。
和其他编程语言不同的是,shell函数在定义是不能指明参数,但是在调用时却可以传递参数,并且给它传递什么参数它就接受什么参数。
shell函数也不限制定义和调用的顺序,你可以将定义放在调用的前面,也可以反过来,将定义放在调用的后面。
1)定义一个函数,输出地址:

//An highlighted block
  1 #!/bin/bash
  2 
  3 function url(){
  4     echo "www.baidu.com"
  5 }
  6 
  7 url

在这里插入图片描述也可以将函数定义放在调用的后面:

//An highlighted block
  1 #!/bin/bash
  2 
  3 url
  4 
  5 function url(){
  6     echo "www.baidu.com"
  7 }

在这里插入图片描述2)定义一个函数,计算所有参数的和:

//An highlighted block
  1 #!/bin/bash
  2 
  3 function getsum(){
  4     local sum=0
  5 
  6     for n in $@
  7     do
  8         ((sum+=n))
  9     done
 10     return $sum
 11 }
 12 getsum 10 20 30 40
 13 echo $?

在这里插入图片描述$@表示函数的所有参数,$?表示函数的退出状态(返回值),此处借用return关键字将返回所有数字的和返回,并使用$?得到这个值,这种方式在shell是非常错误的。
和其他编程语言不同,shell中的函数在定义时不能指明参数,但是在调用时却可以传递参数。
函数参数是shell位置参数的一种,在函数内部可以使用$n来接受,例如,$1表示第一个参数,$2表示第二个参数,以此类推。
除了$n,还有另外三个比较重要的变量:
$#可以获取传递的参数个数,
$@或者$*可以一次性获取所有参数,
$n,$#,$@,$*都属于特殊变量。
1)使用$n来接受函数参数

//An highlighted block
  1 #!/bin/bash
  2 
  3 function show(){
  4     echo "$1"
  5     echo "$2"
  6     echo "$3"
  7     echo "$#"
  8 }
  9 
 10 show a b c

在这里插入图片描述2)使用$@来遍历函数参数

//An highlighted block
  1 #!/bin/bash
  2 
  3 function getsum(){
  4     local sum=0
  5     for n in $@
  6     do
  7         ((sum+=n))
  8     done
  9     echo $sum
 10     return 0
 11 }
 12 
 13 total=$(getsum 10 20 30 40)
 14 echo $total
 15 
 16 #或者
 17 echo $(getsum 10 20 30 40)

在这里插入图片描述

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值