2019-8-24 [Linux] 13.Shell编程 Shell的基本结构 脚本的运行 多命令运行 变量与运算符 数组的使用分支语句 循环结构 字符串操作 函数的应用

文章目录

13.Linux Shell

13.1. Shell 概述

13.1.1 什么是Shell

我们平时所说的 Shell ,可以理解为 Linux 系统提供给用户的使用界面。Shell 为用户提供了输入命令和参数并可得到命令执行结果的环境。当一个用户登录 Linux 之后,系统初始化程序 init 就根据 /etc/passwd 文件中的设定,为每个用户运行一个被称为 Shell(外壳)的程序。
确切地说,Shell 是一个命令行解释器,它为用户提供了一个向 Linux 内核发送请求以便运行程序的界面系统级程序,用户可以用 Shell 来启动、挂起、停止甚至编写一些程序。
Shell 处在内核与外层应用程序之间,起着协调用户与系统的一致性、在用户与系统之间进行交互的作用。
图 1 是 Linux 系统层次结构图,Shell 接收用户输入的命令,并把用户的命令从类似 abed 的 ASCII 码解释为类似 0101 的机器语言,然后把命令提交到系统内核处理;当内核处理完毕之后,把处理结果再通过 Shell 返回给用户。ell 返回给用户。
ell 返回给用户。

在这里插入图片描述
图 1 Linux系统层次结构图

Shell 与其他 Linux 命令一样,都是实用程序,但它们之间还是有区别的。
一旦用户注册到系统后,Shell 就被系统装入内存并一直运行到用户退出系统为止;而一般命令仅当被调用时,才由系统装入内存执行。
与一般命令相比,Shell 除了是一个命令行解释器,同时还是一门功能强大的编程语言,易编写,易调试,灵活性较强。作为一种命令级语言,Shell 是解释性的,组合功能很强,与操作系统有密切的关系,可以在 Shell 脚本中直接使用系统命令。大多数 Linux 系统的启动相关文件(一般在 /etc/rc.d 目录下)都是使用 Shell 脚本编写的。
同传统的编程语言一样,Shell 提供了很多特性,这些特性可以使 Shell 脚本编程更为有用,如数据变量、参数传递、判断、流程控制、数据输入和输出、子程序及中断处理等。
说了这么多,其实我们在 Linux 中操作的命令行界面就是 Linux 的 Shell,也就是 Bash,但是我们的图形界面是 Shell 吗?其实从广义讲,图形界面当然也是 Shell,因为它同样用来接收用户的操作,并传递到内核进行处理。
不过,这里的 Shell 主要指的是 Bash。

13.1.2 Shell的分类

目前 Shell 的版本有很多种,如 Bourne Shell、C Shell、Bash、ksh、tcsh 等,它们各有特点,下面简要介绍一下。
最重要的 Shell 是 Bourne Shell,这个命名是为了纪念此 Shell 的发明者 Steven Bourne。从 1979 年起,UNIX 就开始使用 Boume Shell。Bourne Shell 的主文件名为 sh,开发人员便以 sh 作为 Bourne Shell 的主要识别名称。
虽然 Linux 与 UNIX 一样,可以支持多种 Shell,但 Boume Shell 的重要地位至今仍然没有改变,许多 UNIX 系统中仍然使用 sh 作为重要的管理工具。它的工作从开机到关机,几乎无所不包。在 Linux 中,用户 Shell 主要是 Bash,但在启动脚本、编辑等很多工作中仍然使用 Bourne Shell。

C Shell 是广为流行的 Shell 变种。C Shell 主要在 BSD 版的 UNIX 系统中使用,发明者是柏克莱大学的 Bill Joy。C Shell 因为其语法和 C 语言类似而得名,这也使得 UNIX 的系统工程师在学习 C Shell 时感到相当方便。

Bourne Shell 和 C Shell 形成了 Shell 的两大主流派别,后来的变种大都吸取这两种 Shell 的特点,如 Korn、 tcsh 及 Bash。

Bash Shell 是 GNU 计划的重要工具之一,也是 GNU 系统中标准的 Shell。
Bash 与 sh 兼容,所以许多早期开发出来的 Bourne Shell 程序都可以继续在 Bash 中运行。现在使用的 Linux 就使用 Bash 作为用户的基本 Shell。

Bash 于 1988 年发布,并在 1995-1996年推出Bash 2.0。在这之前,广为使用的版本是1.14,Bash 2.0增加了许多新的功能,以及具备更好的兼容性。下表中详细列出了各版本的具体情况。
在这里插入图片描述 注意:Shell 的两种主要语法类型有 Bourne 和 C,这两种语法彼此不兼容。Boume 家族主要包括 sh、ksh、Bash、psh、zsh;C 家族主要包括 csh、tcsh(Bash 和 zsh 在不同程序上支持 csh 的语法)。

13.2. Shell 使用

13.2.1 echo 命令的使用

学习一种编程语言,写的第一个脚本都是打印"Hello world!",我们也写一个"Hello world!"脚本用来学习一下 Shell 脚本的运行方式。
在写脚本之前,我们需要学习一下 Linux 的打印命令 echo。该命令格式如下:

echo [选项] [输出内容]

选项:

  • e:支持反斜线控制的字符转换(具体参见表 1)

  • n:取消输出后行末的换行符号(内容输出后不换行)

      其实 echo 命令非常简单,命令的输出内容如果没有特殊含义,则原内容输出到屏幕;如果输出内容有特殊含义,则输出打印其含义。
    
【例 1】换行输出与不换行输出
[root@localhost ~]# echo 'hello,world!'
hello,world!
[root@localhost ~]# echo -n 'hello,word!'
hello,word![root@localhost ~]# 

echo命令就是这么简单,输出什么内容,这个内容就会打印到屏幕上。
在 echo 命令中如果使用了"-e"选项,则可以支持字符控制,如表 1 所示。
在这里插入图片描述

【例 2】echo -e参数演示
[root@localhost ~]# echo "你好\t你好"
你好\t你好
[root@localhost ~]# echo -e "你好\t你好"
你好  你好

13.2.2 shell简单使用

做什么事情都会有第一次,我们学习语言的第一次就是写著名的"Hello world!"程序。命令如下:

[root@localhost ~]# mkdir sh
[root@localhost ~]# cd sh
[root@localhost sh]# ll
总用量 0
[root@localhost sh]# touch sh1.sh
[root@localhost sh]# vim sh1.sh
​
添加如下内容 :
#!/bin/bash

# 输出一个hello world
echo 'hello,world!'


大家发现了吗?在 Shell 中是可以直接使用 echo 命令的。
其实不止 echo 命令,所有的 Linux 都可以直接在 Shell 脚本中调用。这种特性使得 Shell 脚本和 Linux 系统结合得更加紧密,也更加方便了 Shell 脚本的编写。
下面给大家解释一下这个脚本的结构。

13.3 Shell的基本结构

第一行"#!/bin/bash"

在 Linux 中,以"#"开头的一般都是注释,不过这句话是例外的。

这句话的作用是标称我以下写的脚本使用的是 Bash 语法,只要写的是基于 Bash 的 Shell 脚本都应该这样开头。

不过,有一些比较喜欢钻研的人也会有疑问,他们在写 Shell 脚本时,不加"#!/bin/bash"这句话,Shell 脚本也可以正确执行。
那是因为我们是在默认 Shell 就是 Bash 的 Linux 中编写的脚本,而且脚本是纯 Bash 脚本才能够正确执行。
如果把脚本放在默认环境不是 Bash 的环境中运行,又或者编写脚本的不是纯 Bash 语言,而是嵌入了其他语言(如 Tcl 语言),那么这个脚本就不能 正确执行了。

第二行:注释

在 Shell 脚本中,除"#!/bin/bash"这行外,其他行只要以"#"开头的都是注释。

第二行就是我们这个脚本的注释,建议大家在写程序时加入清晰而详尽的注释,这些都是建立良好编程规范时应该注意的问题。

第三行:程序的主体

既然 echo 命令可以直接打印"hello,world",那么将这句话放入 Shell 脚本中也是可以正确执行的,因为 Linux 的命令是可以直接在脚本中执行的。
运行方式Shell 脚本写好了,那么这个脚本该如何运行呢?

13.4 在 Linux 中,脚本的运行主要有以下两种方法:

13.4.1 赋予执行权限,直接运行

这种方法是最常用的 Shell 脚本运行方法,也最为直接简单。就是赋予执行权限之后,直接运行。当然,运行时可以使用绝对路径,也可以使用相对路径。命令如下:

[root@localhost sh]# chmod 770 sh1.sh 
[root@localhost sh]# ./sh1.sh 
hello,world!

因为我们已经在~sh目录中,所以也可以使用相对路径运行
Shell 脚本是否可以像 Linux 系统命令一样,不用指定路径,直接运行呢?当然是可以的,不过需要进行环境变量的配置。这里大家只需要知道,我们自己写的 Shell 脚本默认是不能运行的,要么使用绝对路径,要么使用相对路径。

13.4.2 通过Bash调用运行脚本这种方法也非常简单,命令如下:

[root@localhost sh]# cd /etc/
[root@localhost etc]# sh ~/sh/sh1.sh 
hello,world!

这种方法的意思是直接使用 Bash 去解释脚本中的内容,所以这个脚本也可以正常运行。使用这种方法运行脚本,甚至不需要脚本文件有"执行"权限,只要拥有"读"权限就可以运行了。
sh是一个shell。运行sh a.sh,表示我使用sh来解释这个脚本;
如果我直接运行./a.sh,首先你会查找脚本第一行是否指定了解释器,如果没指定,那么就用当前系统默认的shell(大多数linux默认是bash),如果指定了解释器,那么就将该脚本交给指定的解释器;

13.4.2 shell多命令执行

在 Bash 中,如果需要让多条命令顺序执行,则有这样方法,如表 所示。
在这里插入图片描述
";“多命令顺序执行如果使用”;"连接多条命令,那么这些命令会一次执行,但是各命令之间没有任何逻辑关系,也就是说,不论哪条命令报错了,后面的命令仍会依次执行。举个例子:

[root@localhost ~]# ls ; date ; cd /user ; pwd
123456.zip       d1   etc.tar      etc.tar.gz  file1  log   mnt  StudentDAOImpl.java
anaconda-ks.cfg  etc  etc.tar.bz2  etc.zip     file2  log1  sh   vimrc
20190602日 星期日 15:30:57 CST
-bash: cd: /user: 没有那个文件或目录
/root

就是";"的作用,不论前一条命令是否正确执行,都不影响后续命令的执行。再举一个例子:

[root@localhost ~]# date; dd if=/dev/zero of=/root/testfile bs=1k count=100000;date
20190602日 星期日 15:34:13 CST
记录了100000+0 的读入
记录了100000+0 的写出
102400000字节(102 MB)已复制,0.17181 秒,596 MB/20190602日 星期日 15:34:13 CST

dd是 Linux 下的一个非常有用的命令,作用是用指定大小的块拷贝一个文件,并在拷贝的同时进行指定的转换。
/dev/zero 是类 Unix 系统中一个特殊的文件,当读取该文件时,它会提供无限的空字符 null。
它的一个主要用途是提供字符流来初始化数据存储,也就是使用空字符覆盖目标数据。另一个常见的用法是产生一个特定大小的空白文件。
上面的案例可以记录执行一些命令的开始时间与结束时间,当我们需要一次执行多条命令,而这些命令之间又没有可逻辑关系时,就可以使用";"来连接多条命令。

13.4.2.1 "&&"逻辑与

如果使用"&&“连接多条命令,那么这些命令之间就有逻辑关系了。只有第一条命令正确执行了,”&&"连接的第二条命令才会执行。那么,命令 2 是如何知道命令 1 正确执行了呢?
这就需要 Bash 的预定义变量 $? 的支持了,如果 $? 返回值是 0,则证明上一条命令正确执行;如果 $? 返回值是非 0,则证明上一条命令执行错误。
举个例子:

[root@localhost ~]# cp ./test1 /tmp/test1 && rm -rf ./test1 && echo 'ok'!
cp: 无法获取"./test1" 的文件状态(stat): 没有那个文件或目录
#复制/root/test1到/tmp/test1,如果命令成功则删除原文件,并打印"yes"
#因为/root/test1文件不存在,所以第一条命令执行不正确,第二和第三条命令也都不执行
#现在创建这个文件并且赋值并且输出
[root@localhost ~]# touch test1 && echo "hello,java" > test1 && cat test1
hello,java
# 因为这一套命令是可以正确执行,所以会顺序执行

再举个例子,以后我们更换yum源的时候就可以一次性清空 建立缓存 和 更新

[root@localhost ~]# yum clean all && yum makecache && yum -y update

"||“逻辑或
如果使用”||"连接多条命令,则只有前一条命令执行错误,后一条命令才能执行。举个例子:

[root@localhost ~]# cat file3 || touch file3
cat: file3: 没有那个文件或目录
# 没有报错,就创建file3

"&&“和”||"非常有意思,如果我们想要判断某条命令是否正确执行,就可以这样来做:

命令 && echo "yes" || echo "no"

例如:

[root@localhost ~]# ls d2 && echo "ok" || echo "err"
ls: 无法访问d2: 没有那个文件或目录
err

13.4.3 shell 变量和运算符

13.4.3.1 简单变量使用

编写脚本

#!/bin/bash
# 输出时间
now=`date`
echo ${now}
msg1="${now},天气不错,适合出去玩耍"
echo ${msg1}
msg2='${now},天气不错,适合出去玩耍'
echo ${msg2}

执行

[root@localhost sh]# sh -x sh2.sh 
++ date
+ now='2019年 06月 02日 星期日 15:55:45 CST'
+ echo 20190602日 星期日 15:55:45 CST
20190602日 星期日 15:55:45 CST
+ msg1='2019年 06月 02日 星期日 15:55:45 CST,天气不错,适合出去玩耍'
+ echo 20190602日 星期日 15:55:45 CST,天气不错,适合出去玩耍
20190602日 星期日 15:55:45 CST,天气不错,适合出去玩耍
+ msg2='${now},天气不错,适合出去玩耍'
+ echo '${now},天气不错,适合出去玩耍'
${now},天气不错,适合出去玩耍

说明

  1. 变量的定义与使用规则:

    1. 变量名首字符必须为字母,名字只能用字母、数字、下滑线,不要用bash关键字。非法的名字:8var、var-8
    2. 变量定义或赋值时,变量名与值之间用等号连接:变量名=值,等号两边不能有空格,值中有空格或特殊字符需要用双引号或单引号引起来。
    3. 使用 $变量名 或 ${变量名} 形式获取变量值
  2. sh -x sh2.sh
    选项参数:

  • n :不要执行 script,仅查询语法的问题;
  • v :再执行 script 前,先将 scripts 的内容输出到屏幕上;
  • x:实现shell脚本逐条语句的跟踪。这样我们可以分步骤进行查看每步的输出结果
  1. 变量定义中符号的使用
1. 单引号 ' ' :单引号里面的所有字符都是普通字符。
2. 双引号 " ":双引号会保留变量特性,用值替换。
3. 反引号 ` `:位于Esc 键下方,用倒引号括起一个命令时,这个命令将会被执行,执行后的结果作为这个表达式的值。

单引号和双引号用于变量值出现空格时,比如 name=zhang san 这样执行就会出现问题,而必须用引号括起来,比如 name=“zhang san”。

不过,引号有单引号和双引号之分,二者的主要区别在于,被单引号括起来的字符都是普通字符,就算特殊字符也不再有特殊含义;而被双引号括起来的字符中,"$"、"“和反引号是拥有特殊含义的,”$"代表引用变量的值,而反引号代表引用命令。
如果需要调用命令的输出,或把命令的输出赋予变量,则命令必须使用反引号包含,这条命令才会执行,反引号的作用和 $(命令) 是一样的,但是反引号非常容易和单引号搞混,所以推荐大家使用 $(命令) 的方式引用命令的输出。
所以我们的脚本还可以改成

#!/bin/bash
# 输出时间
now=$(date)
echo ${now}
msg1="${now},天气不错,适合出去玩耍"
echo ${msg1}
msg2='${now},天气不错,适合出去玩耍'
echo ${msg2}
  1. 转义符的使用
    编写脚本
#!/bin/bash
msg='hello'
echo ${msg}
echo \${msg}

输出结果

[root@localhost sh]# sh sh3.sh 
hello
${msg}
13.4.3.2 位置变量使用

shell语法中,当调用shell脚本的时候,可以通过脚本后面的位置进行参数传递,然后通过各种不同的位置变量接收即可;
在这里插入图片描述
举个例子 :
编写脚本

#!/bin/bash
echo "脚本的进程ID : $$"
echo "参数脚本名称 : $0"
echo "本次一共传递了 : $# 个参数"
echo "第1个参数是 : $1"
echo "第2个参数是 : $2"
echo "如果想作为字符串接收 : $*"
echo "如果想作为一个数组接收 : $@"
echo "验证一下 : "
echo "\$* 的演示 : "
for i in "$*" ; do
    echo ${i}
done;
echo "\$@ 的演示 : "
for i in "$@" ; do
    echo ${i}
done;
echo "脚本执行状态 : $?"

执行脚本

[root@localhost sh]# sh sh3.sh 赵文明 孙建国 王小花
脚本的进程ID : 40222
参数脚本名称 : sh3.sh
本次一共传递了 : 3 个参数
第1个参数是 : 赵文明
第2个参数是 : 孙建国
如果想作为字符串接收 : 赵文明 孙建国 王小花
如果想作为一个数组接收 : 赵文明 孙建国 王小花
验证一下 : 
$* 的演示 : 
赵文明 孙建国 王小花
$@ 的演示 : 
赵文明
孙建国
王小花
脚本执行状态 : 0

13.5 数组的使用

13.5.1 数组定义

常量列表的方式

#用空格分隔
arr1=("a" "ab" "bc")

直接下标引用,自动定义

arr2[0]=1
arr2[1]=3
arr2[2]=4

declare 命令定义数组

#定义数据arr3,并初始化元素
declare -a arr3=('a' 'b')
​
#先定义,后给元素赋值
declare -a arr3
arr3[0]="a"
arr3[1]="b"

13.5.2 数组使用

获取数组长度:用${#数组名[@或*]} 可以得到数组长度

${#arr[*]} 或 ${#arr[@]}

读取数组元素:

读取下标为1的元素:${arr[1]}
读取数组整个元素:${arr[*]}

给数组元素赋值:

arr[1]=1

删除数组元素:
删除后,数组长度改变

unset arr[1]

数组切片:
直接通过

${数组名[@或*]:起始位置:长度}

切片原先数组,返回是字符串,中间用“空格”分开。
如果加上”()”,将得到切片数组
举例 :

#!/bin/bash
# 数组练习
# 定义一个数组
arr1=("赵文明" "孙建国" "王小花");
# 打印数组长度
echo "数组长度是 : ${#arr1[*]}";
# 遍历数组
for i in ${arr1[*]} ; do
    echo ${i}
done;
# 添加新元素
arr1[3]="李建军"
# 打印一下
echo "新添加元素 : ${arr1[*]}";
# 删除一个元素
unset arr1[0]
# 打印一下
echo "删除元素 : ${arr1[*]}";
echo "数组长度 : ${#arr1[*]}"
# 数组切片
echo ${arr1[*]:2:1}
str=(${arr1[*]:2:1})
# 输出
echo "切片数组 : ${str[*]}"

执行 :

[root@localhost sh]# sh sh5.sh 
数组长度是 : 3
赵文明
孙建国
王小花
新添加元素 : 赵文明 孙建国 王小花 李建军
删除元素 : 孙建国 王小花 李建军
数组长度 : 3
孙建国
切片数组 : 孙建国
[root@localhost sh]# vi sh3.sh 
[root@localhost sh]# sh sh3.sh 
数组长度是 : 3
赵文明
孙建国
王小花
新添加元素 : 赵文明 孙建国 王小花 李建军
删除元素 : 孙建国 王小花 李建军
数组长度 : 3
王小花
切片数组 : 王小花

13.5.3 运算符的使用

13.5.3.1 算术运算符

在这里插入图片描述

其中:
expr 方式:expr 的表达式要用倒引号括起来,并且要用空格分隔
示例:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

13.4.3.1 比较运算符
数字比较

在这里插入图片描述
一般 -eq 这种,用[]
== > < 这种, 用[[]]
示例:
在这里插入图片描述

字符串比较

在这里插入图片描述
示例:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

逻辑运算符
在这里插入图片描述
示例:
在这里插入图片描述

13.4.3.3 文件运算符

在这里插入图片描述
示例:
在这里插入图片描述

判断文档是否存在,如果存在输出“exists”,如果不存在则输出“not exists”
利用短路&& 短路|| 实现
在这里插入图片描述

#!/bin/bash
#判断是否是块设备文件
f="/dev/sr0"
[ -b $f ] && echo 'ok'
​
#判断是否是字符设备文件
f="/dev/null"
[ -c $f ] && echo 'ok'
​
#检测是否是目录
f="/etc"
[ -d $f ] && echo 'ok'
​
#检测是否是普通文件
f="/etc/passwd"
[ -f $f ] && echo "${f}是一个普通文件"
​
#检测是否是符合链接
f="/lib"
[ -L $f ] && echo 'ok' || echo 'no'
​
#检测文件是否可读
f="/etc/passwd"
[ -r $f ] && echo 'ok'
​
#检测文件是否可写
[ -w $f ] && echo 'ok'
​
#检测文件是否可执行
[ -x $f ] && echo 'yes' || echo 'no'
​
#检测文件是否存在
[ -e $f ] && echo 'yes' || echo 'no'

13.6 分支语句

13.6.1 if语句

在这里插入图片描述

if 条件表达式 ; then
    条件判断为true时执行这里
​
fi

13.6.2 if …else

在这里插入图片描述

if 条件表达式 ; then
    条件判断为true时执行这里
else
    条件判断为false时执行这里
fi

13.6.3 if … elif … else

在这里插入图片描述

if  条件表达式一 ; then   
    表达式一true时执行这里
elif  条件判断式二 ; then    
    表达式二true时执行这里
elif  条件判断式三 ; then    
    表达式三true时执行这里
else    
    都不成立时,执行这里
fi

示例:

1) 根据指定日期判断今天是星期几?

实现 :

#!/bin/bash
# 分支结构练习
# 获取现在的星期 0 周日 以此类推
week=$(date -d $1 +%w)
if [ ${week} -eq 0  ] ; then
    echo "今天是星期日";
elif [ ${week} -eq 1 ] ; then
    echo "今天是星期一";
elif [ ${week} -eq 2 ] ; then
    echo "今天是星期二";
elif [ ${week} -eq 3 ] ; then
    echo "今天是星期三";
elif [ ${week} -eq 4 ] ; then
    echo "今天是星期四";
elif [ ${week} -eq 5 ] ; then
    echo "今天是星期五";
elif [ ${week} -eq 6 ] ; then
    echo "今天是星期六";
else 
    echo "输入了一个错误的日期"
fi;


运行 :

[root@localhost sh]# sh sh3.sh asdsadsad
date: 无效的日期"asdsadsad"
sh3.sh:5:[: -eq: 期待一元表达式
sh3.sh:7:[: -eq: 期待一元表达式
sh3.sh:9:[: -eq: 期待一元表达式
sh3.sh:11:[: -eq: 期待一元表达式
sh3.sh:13:[: -eq: 期待一元表达式
sh3.sh:15:[: -eq: 期待一元表达式
sh3.sh:17:[: -eq: 期待一元表达式
输入了一个错误的日期
[root@localhost sh]# sh sh3.sh 2019-06-02
今天是星期日
[root@localhost sh]# sh sh3.sh 2019-06-03
今天是星期一
[root@localhost sh]# sh sh3.sh 2019-06-04
今天是星期二
​
2)从键盘接收密码 ,成功输出用户名;登录失败显示提示信息。

实现

#!/bin/bash
# 接收用户名密码验证
read -p "请输入用户名 : " loginName
read -p "请输入密码 : " -s loginPass
if [[ ${loginNane} -eq 'admin' && ${loginPass} -eq 'admin' ]] ; then
    echo "欢迎您,${loginName}管理员先生";
else 
    echo "很抱歉,登录失败!";
fi;
#!/bin/bash
read -p "请输入用户名:" username
read -p "请输入密码:" -s password
​
if [[ ${username} -eq 'admin' && ${password} -eq '123456'  ]] ; then
        echo -e "\n欢迎您,${username}" ;
else
        echo -e  "\n登录失败";
fi;

效果

[root@localhost sh]# sh sh3.sh 
请输入用户名 : admin
请输入密码 : 欢迎您,admin管理员先生
[root@localhost sh]# sh sh3.sh 
请输入用户名 : admin
请输入密码 : 很抱歉,登录失败!

从键盘接收: read 变量名
Linux read命令用于从标准输入读取数值。

read 内部命令被用来从标准输入读取单行数据。这个命令可以用来读取键盘输入,当使用重定向的时候,可以读取文件中的一行数据。

  • a 后跟一个变量,该变量会被认为是个数组,然后给其赋值,默认是以空格为分割符。
  • d 后面跟一个标志符,其实只有其后的第一个字符有用,作为结束的标志。
  • p 后面跟提示信息,即在输入前打印提示信息。
  • e 在输入的时候可以使用命令补全功能。
  • n 后跟一个数字,定义输入文本的长度,很实用。
  • r 屏蔽\,如果没有该选项,则\作为一个转义字符,有的话 \就是个正常的字符了。
  • s 安静模式,在输入字符时不再屏幕上显示,例如login时输入密码。
  • t 后面跟秒数,定义输入字符的等待时间。
  • u 后面跟fd,从文件描述符中读入,该文件描述符可以是exec新开启的。

13.6.4 case

case $var in
     模式1)  
             代码块1
             ;; 
     模式2)    
             代码块2    
             ;;    
     模式3)    
             代码块3   
             ;;)    
             代码块4    
             ;;
esac

案例 : 优化 判断今天是星期几的代码

实现

#!/bin/bash
# 编写shell完成对判断是星期几的优化
# 获取现在的星期 0 周日 以此类推
week=$(date -d $1 +%w)
case ${week} in
    0)
        echo "今天是星期日";;
    1)
        echo "今天是星期一";;
    2)
        echo "今天是星期二";;
    3)
        echo "今天是星期三";;
    4)
        echo "今天是星期四";;
    5)
        echo "今天是星期五";;
    6)
        echo "今天是星期六";;
    *)
        echo "输入的日期有误";
esac;
case ${week} in
    0 | 6)
        echo "周末了抓紧时间嗨";;
    [1-5])
        echo "抓紧时间工作上班";;
    *)
        echo "输入的日期有误";
esac;

运行

[root@localhost sh]# sh sh3.sh 2019-06-02
今天是星期日
周末了抓紧时间嗨
[root@localhost sh]# sh sh3.sh 2019-06-03
今天是星期一
抓紧时间工作上班
[root@localhost sh]# sh sh3.sh 2019-06-04
今天是星期二
抓紧时间工作上班
​

13.7 循环结构

13.7.1 for 循环变量 in 数据列表

for 循环变量 in 数据列表
do
    循环体代码段
done

这里的数据列表 是一系列以空格分隔的值,shell每次从这个列表中取出一个值,然后运行do/done 之间的命令。

[root@localhost sh]# cat sh1.sh 
#!/bin/bash
# for循环应用
for i in 1 2 3 4 5 ; do
    echo ${i}
done;

执行结果:

[root@localhost sh]# sh sh1.sh 
1
2
3
4
5

示例:

1)根据输入的参数,如果是目录,就列出目录下的文档,如果不是目录,就报错退出

实现

#!/bin/bash
# 根据输入判断是否是目录 如果是目录展示这个目录下的内容 如果不是目录给出提示
# 接收参数
path1=$*
# 判断
if [ ! -d ${path1} ] ; then
    echo "这不是一个目录,你在逗我???";
    exit;
fi;
​
# 如果是一个目录则遍历循环
for i in `ls ${path1}` ; do
​
    echo ${i};
​
done;


效果

[root@localhost sh]# sh sh3.sh .
sh1.sh
sh2.sh
sh3.sh
[root@localhost sh]# sh sh3.sh sh3.sh 
这不是一个目录,你在逗我???

2)以数组的方式接收上面ls命令的返回值然后再遍历

实现

#!/bin/bash
# 根据输入判断是否是目录 如果是目录展示这个目录下的内容 如果不是目录给出提示
# 打印
echo "输入的目录位置为 : $*"
# 接收参数
path1=$*
# 判断
if [ ! -d ${path1} ] ; then
    echo "这不是一个目录,你在逗我???";
    exit;
fi;
​
arr1=(`ls ${path1}`);
​
​
# 如果是一个目录则遍历循环
for i in ${arr1[*]} ; do
​
    echo ${i};
​
done;

效果 :

[root@localhost sh]# sh sh3.sh /
输入的目录位置为 : /
bin
boot
data
dev
etc
home
lib
lib64
media
mnt
opt
proc
root
run
sbin
srv
sys
tmp
userhome
usr
var

13.7.2 for (( 控制变量的初始化; 循环的条件; 循环控制变量的更新 ))

#满足条件则循环否则退出
for (( 控制变量的初始化; 循环的条件; 循环控制变量的更新 )) 
do
     循环体代码段
done

示例:

1)计算从1加到10,并输出结果;统计偶数的个数,并输出结果

实现

#!/bin/bash
# for循环应用
# 计算从1加到10 并输出结果
​
# 求和
sum=0;
​
# 统计
count=0;
for(( i=1;i<=10;i++  ));do
    # 计算求和  
    sum=$((sum+i));
    # 判断奇数和
    if [ $((i%2)) == 0 ] ; then 
        ((count++))
    fi;
​
​
done;
​
echo "1-10 总数和${sum} 其中偶数的个数为 ${count}"

执行结果:

[root@localhost sh]# sh sh1.sh 
1-10 总数和55 其中偶数的个数为 5
2)从外面带参数进入,计算从1到这个参数的累加,并输出;如果参数小于1,则报数据错误。

实现 :

#!/bin/bash
# for循环应用
# 计算从1加到N 并输出结果
​
​
# 判断
if [ $* -lt 1  ];then
    echo "传入的值必须大于1!";
    exit;
fi; 
​
​
# 求和
sum=0;
​
# 统计
count=0;
for(( i=1;i<=$*;i++  ));do
    # 计算求和  
    sum=$((sum+i));
    # 判断奇数和
    if [ $((i%2)) == 0 ]; then
    
        ((count++))
    fi;
​
​
done;
​
echo "1-$* 总数和${sum} 其中偶数的个数为 ${count}"

执行结果:

[root@localhost sh]# sh sh1.sh 10
1-10 总数和55 其中偶数的个数为 5
[root@localhost sh]# sh sh1.sh 100
1-100 总数和5050 其中偶数的个数为 50

13.7.3 while

条件判断表达式,满足条件则循环,否则退出

while  循环条件
do 
    循环体代码段
done

循环条件为真(值为非0)执行循环体;
循环条件为假(值为0)退出循环体;
示例:
从外面带参数进入,计算从1到这个参数的累加,并输出;如果参数小于1,则报数据错误。
实现 :

#!/bin/bash
# for循环应用
# 计算从1加到N 并输出结果
​
​
# 判断
if [ $* -lt 1  ];then
    echo "传入的值必须大于1!";
    exit;
fi; 
​
​
# 求和
sum=0;
​
# 统计
count=0;
# 定义循环变量
index=1;
​
​
# 开始循环
​
while [ ${index} -le $*  ] 
do
​
    # 计算数据总和
    sum=$((sum+index));
    # 计算偶数个数
    if [ $((index%2)) == 0  ] ; then
        ((count++))
    fi;
    # 循环计数器叠加
    ((index++));
​
done;
​
​
​
echo "1-$* 总数和${sum} 其中偶数的个数为 ${count}"


执行结果:

[root@localhost sh]# sh sh1.sh 10
1-10 总数和55 其中偶数的个数为 5
[root@localhost sh]# sh sh1.sh 100
1-100 总数和5050 其中偶数的个数为 50


((index++)): 如果i值自增,可用此写法,也可以 写成 index=$((index + 1))

13.8 字符串操作

判断读取字符串值
在这里插入图片描述
在这里插入图片描述
读取、截取、长度、替换

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

13.9 Shell 函数的应用

函数(有返回值(只能是int类型0或非0 脚本执行是否成功)的方法) 和 过程(没有返回值的方法) 的区

1) 不带有返回值的定义及使用

定义格式:

# 不带有返回值的函数
function 函数名称()
{
    语句块
}
函数名称()
{
    语句块
}
​
使用函数的时候
函数名称 参数1 参数2 参数3 .... 参数N

2)带有返回值的定义及使用

定义格式:

function 函数名称()
{
    语句块
    echo 返回结果 或者 return(推荐) 返回结果
}
或者
函数名称()
{
    语句块
    echo 返回值 或者 return(推荐) 返回结果
}

案例 :
定义一个无返回值的函数
在这里插入图片描述
注意 : 方法中的位置变量 和 脚本的位置变量一定要分清
如果返回值是数字的 可以使用return
在这里插入图片描述
这里需要注意 return 只能返回数字 不能返回其他 如果想返回其他 需要使用echo 并且注意它的调用方式
在这里插入图片描述
这个就是返回字符串的echo方式的函数编写

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值