Shell脚本编程
当命令或者程序语句写在文件中,我们执行文件,读取其中的代码,这个程序文件就称为shell脚本在shell脚本里定义多条Linux命令以及循环控制语句,然后将这些linux命令一次性执行完毕,执行脚本文件的方式称之为非交互式。windows中存在*.bat的批处理脚本,Linux中常用*.sh脚本文件。在Linux系统中,shell脚本或称之为(bash shell程序)通常都是由vim编辑,由Li
目录
什么是shell
前言
- shell是一块包裹着系统核心的壳子,处于操作系统的最外层,与用户直接对话,把用户的输入,解释给操作系统,然后处理操作系统的输出结果,输出到屏幕给与用户看到结果
- shell是一个用C语言编写的程序,他是用户使用linux的桥梁,shell既是一种命令语言,又是一款程序设计语言
- shell是指一种应用程序,这个应用程序提供了一个界面,用户通过这个界面访问操作系统内核服务
注意:从我们登录linux,输入账号密码到进入linux交互式界面,所有的操作,都是交给shell解释并执行的。
具体流程
图形化界面与命令行界面的区别
- 命令行操作:shell解释执行后,输出结果到黑屏命令行界面
- 图形化操作:shell接受点击动作,输出图案数据
shell脚本
前言
含义:当命令或者程序语句写在文件中,我们执行文件,读取其中的代码,这个程序文件就称为shell脚本
非交互方式:在shell脚本里定义多条Linux命令以及循环控制语句,然后将这些linux命令一次性执行完毕,执行脚本文件的方式称之为非交互式。
注意:
- windows中存在*.bat的批处理脚本,Linux中常用*.sh脚本文件。
- 在Linux系统中,shell脚本或称之为(bash shell程序)通常都是由vim编辑,由Linux命令、bash shell指令,逻辑控制语句和注释信息组成。
脚本语言
- shell脚本语言属于一种弱类型语言,无需声明变量类型,直接定义使用。
- shell语言定义的变量,数据类型默认都是字符串类型
脚本注释
- 在shell脚本中,#后面的内容代表注释内容,提供给开发者使用或观看,系统会忽略此行
- 注释可以单独写一行,也可以跟在命令后面
Shebang
前言:计算机程序中,shebang指的是出现在文本文件的第一行前两个字符(#!)
在Unix系统中,程序会分析shebang后面的内容,作为解释器的指令(就是该文件用什么解释器去读)
- 以#!/bin/bash开头的文件,程序在执行的时候会调用/bin/sh,也就是bash解释器
- 以#!/usr/bin/python开头的文件,代表指定python解释器去执行
- 以#!/usr/bin/env 解释器名称,其是一种在不同平台上都能正确找到解释器的办法
注意:
- 若脚本未指定shebang,则脚本执行的时候,默认使用当前shell去解释脚本,即:$SHELL
- 若#!指定的解释器不是一个可执行文件,那么指定的解释器会被忽略转交给当前shell解释器去执行
- #!之后的解释器需要写绝对路径,因为不会自动到$PATH中寻找解释器
- 若在解释文件时,你指定了解释器来解释脚本文件,那么#!这一行会被忽略,解释器用你在命令行指定的解释器
脚本案例
创建hello的脚本:vim hello.sh
[root@CentOS7-1 ~]# cat hello.sh
#!/bin/sh
#我是个注释,以下是脚本命令
echo "我是shell脚本的一行代码"
执行shell脚本的方法
- 给足权限,执行具体路径下的具体文件:./hello.sh
- 指定解释器来执行脚本:/bin/bash ./hello.sh
- 使用source或(.)命令:source hello.sh或. hello.sh
- 使用重定向解释符:/bin/bash < hello.sh
注意:
- 每次调用bash/sh解释器来执行脚本,都会开启一个shell,因此不保留当前shell变量,并且脚本执行完退出到之前shell;调用source或者点符号,在当前shell环境下加载脚本,因此保留变量(通过pstree命令检查进程树)
- 在shell脚本中若有一个环节出错,那么还会往下继续执行,若想退出,那么用关键字exit(状态码不管给的是0或1都会退出)
父子shell
父shell
含义:我们通过虚拟的终端远程链接服务器时,默认的交互式环境就称为父shell
执行:pstree
结果:├─sshd───sshd───bash───pstree
执行:ps -ef --forest(查看父shell)
root 1987 1 0 08:07 ? 00:00:00 /usr/sbin/sshd
root 2704 1987 0 17:06 ? 00:00:00 \_ sshd: root@pts/0
root 2706 2704 0 17:06 pts/0 00:00:00 \_ -bash
root 2723 2706 0 17:06 pts/0 00:00:00 \_ ps -ef --forest
子shell
含义:父shell的情况下,执行sh或bash命令就会进入子shell,按exit退出当前子shell
开启子shell命令:sh/bash
执行: ps -ef --forest(查看子shell)
root 1987 1 0 08:07 ? 00:00:00 /usr/sbin/sshd
root 2558 1987 0 13:23 ? 00:00:00 \_ sshd: root@pts/1
root 2560 2558 0 13:23 pts/1 00:00:00 \_ -bash
root 2687 2560 0 17:01 pts/1 00:00:00 \_ bash
root 2701 2687 0 17:03 pts/1 00:00:00 \_ ps -ef --forest
注意:子shell也可以再有子shell(子shell嵌套)
创建进程列表
含义
前言:shell的进程列表理念需要使用小括号,如下执行方式就被称之为进程列表,加上小括号,就是开启在子shell中运行(小括号可以加多个)
例子:(cd ~;pwd;ls;cd /tmp/;pwd;ls)
检测是否在子shell环境中
- linux默认的有关shell的变量——BASH_SUBSHELL;
- BASH_SUBSHELL变量值的特点:若为0,就是在当前shell环境中执行的,否则就是开辟子shell去运行的,值为几就是开辟几个shell
执行:cd ~;pwd;ls;cd /tmp/;pwd;ls;echo $BASH_SUBSHELL
结果:BASH_SUBSHELL的值为0
执行: (cd ~;pwd;ls;cd /tmp/;pwd;ls;echo $BASH_SUBSHELL)
结果:BASH_SUBSHELL的值为1
子shell嵌套
执行:(pwd;(echo $BASH_SUBSHELL))
结果:BASH_SUBSHELL的值为2
内置命令与外置命令
含义
- 内置命令:在系统启动时就加载入内存,常驻内存,执行效率更高,但是占用资源
- 外置命令:需要从硬盘中读取程序文件,再读入内存加载
注意:
- 外置命令也称之为自己单独下载的文件系统命令,处于bash shell之外的程序
- 外置命令一定会开启子进程执行,内置命令不会产生子进程去执行
- 内置命令和shell是一体的,是shell的一部分,不需要单独去读取某个文件,系统启动后就执行在内存中了
内置外置命令的区分
语法:type 命令
[root@CentOS7-1 tmp]# type cd
cd is a shell builtin
[root@CentOS7-1 tmp]# type ps
ps is hashed (/usr/bin/ps)
查看linux的内置shell命令:compgen -b
bash基础特性
bash是什么
- bash是一个命令处理器,运行在文本窗口中,并能执行用户直接输入的命令
- bash还能从linux文件中读取linux命令,称之为脚本
- bash支持通配符、管道、命令替换、条件判断等逻辑控制语句
bash特性汇总
- 文件路径或命令tab补全
- 快捷键ctrl+a,e,u,k,l
- 通配符
- 命令历史
- 命令别名
- 多命令一行执行
命令历史
前言:shell会保留其会话中用户提交执行的命令
语法:history [options]
options
- 什么都没有,查看历史命令
- -C:清空历史命令
- -r ~/.bash_history:恢复历史命令
查看历史命令条数:echo $HISTSIZE
注意:
- echo是shell语言的输出命令
- $变量为取出该变量的值
- 我们打的这些命令其实记录在~/.bash_history文件中
调用历史命令
[root@CentOS7-1 ~]# history
1 ping www.baidu.com
2 vi /etc/sysconfig/network-scripts/ifcfg-ens33
3 clear
4 ipaddr
5 ip addr
6 ping www.baidu.com
7 systemctl restart network.service
8 ip addr
9 ping www.baidu.com
10 cls
调用方法:!历史命令id号
[root@CentOS7-1 ~]# !8
ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
link/ether 00:0c:29:36:f4:28 brd ff:ff:ff:ff:ff:ff
inet 192.168.188.100/24 brd 192.168.188.255 scope global ens33
valid_lft forever preferred_lft forever
inet6 fe80::aba2:1be3:78e2:8a7f/64 scope link
valid_lft forever preferred_lft forever
执行上次的命令:!!
快捷键
- ctrl+a:命令行光标移到行首
- ctrl+e:命令行光标移到行尾
- crtl+←/→:光标按一个单词间隔向左右移动
- ctrl+↑/↓:查看历史命令
- ctrl+u:从光标当前位置删除到行首
- ctrl+k:从光标当前位置删除到行尾
- ctrl+l:清空屏幕
命令别名
前言:别名,即给这个命令起了一个小名。别名的好处就是让服务器按照你的习惯来进行命令的命名和使用。别名命令的优先级高于原有命令的优先级。
设置别名:alias 别名=‘原命令’
查询所有别名:alias
删除别名:unalias 别名
注意:
- 通过命令行设置的别名只会在当前shell进程临时生效,一旦重启系统这个别名就会消失
- 若想要别名永久生效就在用户目录下的.bashrc中设置别名
bash多命令一行执行
前言:bash支持命令行多命令一行执行,每个命令之间用;隔开
[root@CentOS7-1 /]# cd /;pwd;ls
/
bin boot dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var
shell注释
单行注释:#注释内容
多行注释
:<<!
注释内容
注释内容
!
注意:多行注释的!可以换成任何内容,但是上下的内容必须一样
[root@CentOS7-1 ~]# /bin/bash ./explain.sh
分隔符
实验结束
[root@CentOS7-1 ~]# cat explain.sh
#! /bin/sh
#我就是个单行注释
echo "分隔符"
:<<!
我是个多行注释
实验中
!
echo "实验结束"
shell变量
变量:变量是暂时存放数据的部分,是一种数据标记(房间号,标记了客人所在的位置)数据存储在内存空间,通过正确的变量名字即可取出对应的值
定义变量规则
- 变量命名只能使用英文字母、数字和下划线,首个字母不能以数字开头
- 中间不能有空格,可以使用下划线
- 严格区分大小写
- 不能使用标点符号,不能使用bash里的关键字
变量的定义与输出
变量定义:变量名="变量值"
变量输出:echo ${变量名}
[root@CentOS7-1 ~]# cat var.sh
#! /bin/bash
#定义name变量,双引号不写也行
name="lili"
#输出name变量的内容两种方式,第一种是简写
echo $name
echo ${name}
[root@CentOS7-1 ~]# /bin/bash ./var.sh
lili
lili
注意:
- $变量名(取出变量的值)
- bash默认把所有变量都认为是字符串,变量一般放在{}里面不会被当作字符串
- 变量和值和赋值运算符之间不得有空格
- bash变量是弱类型,无需事先声明类型,是将声明和赋值同时进行
删除变量
语法:unset 变量名
注意:不可以删除一个只读变量(语法:readonly 变量名)
变量的作用域
- 本地变量,只是针对当前shell进程(在命令行中定义的变量)
- 局部变量,针对在shell函数中定义(作用域只在当前函数里,必须使用local关键字)
- 环境变量,也称全局变量,针对当前shell以及任意的子进程都能使用
环境变量
含义:环境变量一般指的是用export内置命令导出的变量,用于定义shell的运行环境,保证shell命令的正确执行。shell通过环境变量确定登陆的用户名、path路径,文件系统等
注意:环境变量可以在命令行中临时创建,但是用户退出shell终端,变量即丢失,若要永久生效,需要修改环境变量配置文件(开机启动过后加载的文件)
环境变量文件位置
- 用户配置文件:~/.bash_profile、~/.bashrc
- 全局配置文件:/etc/profile、/etc/bashrc(且系统建议最好创建在/etc/profile.d而非直接修改主文件)
环境变量的命令
- set:输出所有变量,包括全局变量,局部变量
- env:只显示全局变量
- declare:输出所有的变量,如同set
- export:显示和设置环境变量
环境变量文件加载顺序
特殊的shell扩展变量
前言:扩展变量的语法基本上是处理变量值不存在的情况。
若param变量值为空,返回word字符串,赋值给res变量
res=${param:-word}
若param变量值为空,则word替代变量,且返回其值
res=${param:=word}
若param变量为空,word当作stderr'输出,(不为空)否则输出变量值
用于设置变量为空导致错误时,返回的错误信息
res=${param:?word}
若param变量为空,什么都不做,否则返回word
res=${param:+word}
declare关键字
前言
- declare关键字主要用于声明变量的类型
- shell所有变量声明默认都是字符串类型
语法:declare [+/-] [options] 变量名
- +:取消变量的设定类型
- -:给变量设定类型
options
- a:将变量声明为数组型(+取消设定类型不可用)
- i:将变量声明为整数型
- x:将变量声明为环境变量
- r:将变量声明为只读变量(+取消设定类型不可用)
- p:显示指定变量被声明的类型(没有+取消设定类型)
注意:一旦将变量声明为只读属性,那么不可取消
具体案例
#设定和取消环境变量
[root@CentOS7-1 ~]# app=taobao
[root@CentOS7-1 ~]# declare -p app
declare -- app="taobao"
[root@CentOS7-1 ~]# declare -x app
[root@CentOS7-1 ~]# declare -p app
declare -x app="taobao"
[root@CentOS7-1 ~]# declare +x app
[root@CentOS7-1 ~]# declare -p app
declare -- app="taobao"
#设定和声明数组
[root@CentOS7-1 ~]# ball=basketball
[root@CentOS7-1 ~]# echo ${ball}
basketball
[root@CentOS7-1 ~]# declare -a ball
[root@CentOS7-1 ~]# echo ${ball[@]}
basketball
[root@CentOS7-1 ~]# echo ${ball[0]}
basketball
[root@CentOS7-1 ~]# ball[1]=football
[root@CentOS7-1 ~]# echo ${ball[@]}
basketball football
export关键字
作用:可以将当前进程的变量传递给子进程去使用(将来配置profile的时候,所有的变量前必须加export)
使用方法:export name=lili
理解:首先从当前进程查询name变量,若当前进程没有这个变量,那么默认就会去父进程查询这个name变量(若我们用export修饰变量,那么这个变量就对子进程也可见)
使用场景
提前的hello.sh脚本
[root@CentOS7-1 ~]# cat hello.sh
#!/bin/sh
echo "start"
echo $name
结论:我们已经知道,这种启动方式开启bash后会新开一个进程执行本脚本命令,所以变量接收不到,但是加了export关键字后就能解决该问题。
local关键字
前言:local关键字只能用在函数中
不使用local关键字定义变量
[root@CentOS7-1 ~]# cat text.sh
#! /bin/sh
func(){
a=88
}
#调用函数
func
echo "a=$a"
结果:执行该脚本后(/bin/sh text.sh),最终结果为a=88,可见在函数中普通变量也是本地变量
使用local关键字定义变量
[root@CentOS7-1 ~]# cat text.sh
#! /bin/sh
func(){
local a=88
}
#调用函数
func
echo "a=$a"
结果:执行该脚本后(/bin/sh text.sh),最终结果为a=,由此观之,在函数中变量用local关键字后就成了局部变量
shell字符串
前言:字符串是shell编程中最常用最有用的数据类型,字符串可以用单引号,也可以用双引号,也可以不用引号
不同引号的区别
单引号
- 单引号里的任何字符都会原样输出,单引号字符中的变量是无效的,不能识别特殊语法
- 单引号字符串中不能出现单独一个单引号,但可以成对出现,作为字符串拼接使用
双引号
- 双引号中可以出现变量,内部可以识别特殊语法
- 双引号里可以出现转义字符
反引号
- 反引号里面写上变量或者linux的命令,那么反引号中的命令执行结果会被保留下来
区别案例
双引号与单引号中的变量
[root@CentOS7-1 ~]# username=linda
[root@CentOS7-1 ~]# word="hello,$username"
[root@CentOS7-1 ~]# echo $word
hello,linda
[root@CentOS7-1 ~]# say='hello,$username'
[root@CentOS7-1 ~]# echo $say
hello,$username
双引号与反引号
[root@CentOS7-1 ~]# echo "pwd"
pwd
[root@CentOS7-1 ~]# echo `pwd`
/root
注意:和反引号具有类似功能的还有$(命令)
计算字符串的长度
语法:echo ${#变量名}
[root@CentOS7-1 ~]# username=linda
[root@CentOS7-1 ~]# echo ${#username}
5
shell子串
内容
格式 | 说明 |
${变量} | 返回变量的值 |
${#变量} | 返回变量长度(字符长度) |
${变量:start} | 返回变量start(数字,从0开始)自身以及之后的字符 |
${变量:start:length} | 提取start(包括自身)之后length长度的字符 |
${变量/pattern/string} | 用string代替第一个匹配到的pattern |
${变量//pattern/string} | 用string代替变量所有的pattern |
${变量#word} | 从变量开头删除最短匹配的word子串(必须和开头匹配上) |
${变量##word} | 从变量开头删除最长匹配的word子串(必须和开头匹配上) |
${变量%word} | 从变量结尾删除最短匹配的word子串(必须和结尾匹配上) |
${变量%%word} | 从变量结尾删除最长匹配的word子串(必须和结尾匹配上) |
注意:这里面的替换和删除不改变原有变量的值
子串案例
[root@CentOS7-1 ~]# word="i like it you like it"
[root@CentOS7-1 ~]# echo $word
i like it you like it
[root@CentOS7-1 ~]# echo ${#word}
21
[root@CentOS7-1 ~]# echo ${word:10}
you like it
[root@CentOS7-1 ~]# echo ${word:2:4}
like
[root@CentOS7-1 ~]# echo ${word/like/hate}
i hate it you like it
[root@CentOS7-1 ~]# echo ${word//like/hate}
i hate it you hate it
shell数组
前言
- bash支持一维数组(不支持多维数组),并没有限定数组大小
- 数组元素下标由0开始编号。获取数组中的元素要利用下标,下标可以是整数或算数表达式,其值应该大于等于0
- shell数组用括号来表示,元素用空格符号分隔开
定义数组:数组名=(值1 值2 值3 …… 值n)
读取数组:接受变量=${数组名[下标]}
修改/增加数组元素:数组名[下标]=新值
删除数组:unset 数组名
获取数组中的所有元素:
- echo ${数组名[@]}
- echo ${数组名[*]}
获取数组长度:
- length=${#数组名[@]}
- length=${#数组名[*]}
数组的遍历
for 接受变量 in ${数组名[@]}
do
echo $接受变量;
done
具体案例
[root@CentOS7-1 ~]# favs=("basketball" "football" "table tennis" "tennis")
[root@CentOS7-1 ~]# echo ${favs[1]}
football
[root@CentOS7-1 ~]# favs[4]="rugby"
[root@CentOS7-1 ~]# echo ${favs[@]}
basketball football table tennis tennis rugby
[root@CentOS7-1 ~]# echo ${#favs[*]}
5
[root@CentOS7-1 ~]# for ball in ${favs[@]};do echo $ball;done
basketball
football
table
tennis
tennis
rugby
[root@CentOS7-1 ~]# unset favs
[root@CentOS7-1 ~]# echo ${favs[@]}
[root@CentOS7-1 ~]#
bash基础的内置命令
内置命令汇总
echo——输出数据
eval——执行多条语句
exec——执行后登出
export——查询环境变量
read——输入数据
shift——移动光标
read命令
前言:用于将用户在键盘输入的内容保存到变量中
语法:read [options] 变量名1 变量名2
options
- -p 给用户看到的提示信息:可以让用户看到提示
- -t 数字:等待用户输入的超时时间(单位秒)
[root@CentOS7-1 ~]# read num
23
[root@CentOS7-1 ~]# echo $num
23
[root@CentOS7-1 ~]# read -p "请输入您的内容" word
请输入您的内容welcome
[root@CentOS7-1 ~]# echo $word
welcome
[root@CentOS7-1 ~]# read -t 20 -p "请输入您的名字,年龄:" yourname yourage
请输入您的名字,年龄:cjc 18
[root@CentOS7-1 ~]# echo $yourname $yourage
cjc 18
注意:read语法可以接受多个变量名,不同变量名用空格隔开,包括用户输入的变量内容也要用空格隔开
echo命令
前言:shell的echo指令主要用于字符串的输出,默认换行输出
语法:echo [options] 字符串1 字符串2
options
- -n:不换行输出
- -e:解析字符串中的特殊符号(如回车\r、换行\n)
[root@CentOS7-1 ~]# name=lili
[root@CentOS7-1 ~]# echo "hello $name"
hello lili
[root@CentOS7-1 ~]# echo `date`
Sat Jul 29 13:11:27 CST 2023
[root@CentOS7-1 ~]# echo -e "hello \n"
hello
[root@CentOS7-1 ~]# echo "hello" `date`
hello Sun Jul 30 10:02:16 CST 2023
[root@CentOS7-1 ~]# printf "您好\n我是吴彦祖"
您好
我是吴彦祖[root@CentOS7-1 ~]#
注意:输出命令还有一个printf命令,其内部可以自动识别特殊符号。
eval命令
前言:
- 表示执行多个命令。
- 每个命令之间用;隔开
[root@CentOS7-1 /]# eval ls;cd root
bin boot dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var
[root@CentOS7-1 ~]#
理解:将后面的命令,当作参数传给eval,然后eval再去执行这些命令
exec命令
前言:不创建子进程,执行后续命令,且执行完毕后自动exit
shell参数传递
前言
- 执行shell脚本时,向脚本传递参数,脚本内获取参数的格式为:$n(获取到的第n个参数),n代表一个数字($0为脚本文件名)
- 脚本传递的参数通过空格与脚本文件分开
- 若向脚本传递多个参数时每个参数之间用空格隔开
参数接受处理
$@:以多个字符串的形式显示所有的参数
$_:在此之前执行的命令最后的一个参数
注意:返回上一个命令的状态码可以用exit 状态码,他通常和$?一起使用
具体案例
[root@CentOS7-1 ~]# cat param.sh
#! /bin/sh
#脚本文件名
echo "hello $0"
#脚本文件传递的第一个参数
echo "hello $1"
#脚本文件传递的第二个参数
echo "hello $2"
#传递参数的个数
echo $#
#以一个单字符串显示所有向脚本传递的参数
echo $*
#脚本运行当前的进程id号
echo $$
#后台运行最后一个进程的id号
echo $!
#显示最后命令的退出状态,0表示没有错误
echo $?
[root@CentOS7-1 ~]# /bin/sh ./param.sh param1 param2
hello ./param.sh
hello param1
hello param2
2
param1 param2
2818
0
shell函数
前言
- linux shell可以用户定义函数,然后就可以在shell脚本中通过函数名随便调用
- 函数就是将你需要执行的shell命令组合起来,组合成一个函数体(还得给该函数体起一个名字叫做函数名)
- 参数返回值,可以加return返回,若不加,将以最后一条命令运行结果(数字id)作为返回值
- 使用source或点执行函数脚本文件后,那么该函数在命令行也可以使用
- 在函数内可以使用local关键字定义变量,这样的变量叫做局部变量只能在函数内使用
return和exit的区别
- return是结束函数的执行,返回一个(退出值/返回值)
- exit是结束shell环境,返回一个(退出值/返回值)给当前shell
- return只能写在函数中,而exit为shell内置命令写在哪都可以
shell函数语法
函数定义
function 函数名(){
你想执行的命令序列
return 返回值
}
注意:函数名前面的function关键字可以省略
函数的调用
函数名
注意:
- 函数的调用直接用函数名,不用加小括号。
- 函数必须先定义,再执行
- 函数的返回值接受用$?参数,其为数字id,范围为0-255之间。
具体案例
[root@CentOS7-1 ~]# cat func.sh
#! /bin/sh
#函数的定义
function funcReturn(){
echo "对输入的两个数进行相加运算"
echo "输入第一个数字"
read aNum
echo "输入第二个数字"
read bNum
echo "这两个数字分别为 $aNum 和 $bNum !"
return $(($aNum+$bNum))
}
#函数调用
funcReturn
#接受函数的返回值
echo "接受的函数返回值为 $? "
#执行脚本
[root@CentOS7-1 ~]# /bin/sh ./func.sh
对输入的两个数进行相加运算
输入第一个数字
2
输入第二个数字
5
这两个数字分别为 2 和 5 !
接受的函数返回值为 7
函数的传参与接受参数
函数的传参
函数名 参数1 参数2 …… 参数n
注意:
- 参数的传递中参数要与函数名用空格隔开,多个参数之间用空格隔开
- 在函数内接受参数用$n来表示接受函数内的第n个参数
- 在命令行中传递的参数不会在函数内被接收,除非将在命令行的参数变为函数的参数
具体案例
[root@CentOS7-1 ~]# cat func.sh
#! /bin/sh
funparam(){
echo "第一个参数为$1"
echo "第二个参数为$2"
echo "第五个参数为${5}"
echo "参数总共有$#个"
echo "作为一个字符串输出所有参数$*"
}
#函数调用并传参
funparam 1 2 3 4
#执行脚本
[root@CentOS7-1 ~]# /bin/sh ./func.sh
第一个参数为1
第二个参数为2
第五个参数为
参数总共有4个
作为一个字符串输出所有参数1 2 3 4
命令行参数与函数参数的关系
[root@CentOS7-1 ~]# cat func.sh
#! /bin/sh
#函数的定义
function funcReturn(){
echo "传递的第一个参数 $1"
echo "传递的第二个参数 $2"
}
#函数调用
funcReturn
#接受函数的返回值
echo "接受的函数返回值为 $? "
[root@CentOS7-1 ~]# source func.sh 1 2
传递的第一个参数
传递的第二个参数
接受的函数返回值为 0
结果:由此观之,在命令行中传递的参数在函数中没有生效。
shell数值计算
算数运算符
前言:shell的一些基础命令只支持整数运算,小数的运算需要bc这样的命令才支持
特殊符号运算
++
- a++:a先参与运算,a再加一
- ++a:a先加一再参与运算
--
- a--:a先参与运算,a再减去一
- --a:a先减去一再参与运算
常见的算数运算符命令
(())运算
注意:对于(())计算、比较在内部,取值在外部用$来取值
let命令运算
前言:let命令的执行,效果等同于双小括号,但是双小括号效率更高。
let实践
[root@CentOS7-1 ~]# num=5
[root@CentOS7-1 ~]# let num=num+8
[root@CentOS7-1 ~]# echo $num
13
[root@CentOS7-1 ~]# num=5
[root@CentOS7-1 ~]# num=$num+8
[root@CentOS7-1 ~]# echo $num
5+8
注意:let命令用于执行一个或多个表达式,变量计算中不需要加上$来表示变量(let后面会自动进行语法转义)
expr命令
前言:expr是一款表达式计算工具,使用它能够完成表达式的求值操作
expr执行加减乘除
[root@CentOS7-1 ~]# a=10
[root@CentOS7-1 ~]# b=20
[root@CentOS7-1 ~]# val=`expr $a + $b`
[root@CentOS7-1 ~]# echo $val
30
[root@CentOS7-1 ~]# mul=`expr $a \* $b `
[root@CentOS7-1 ~]# echo $mul
200
注意:
- 包括expr以及在内的算式全用反引号包裹才能把值赋给变量
- expr必须要以传入参数的形式,并且变量与运算符之间用空格隔开
- expr命令并不是很好用,其基于空格传入参数,但在shell里有一些字符具有特殊意义,需要用转义字符表达
expr求长度
语法:expr length 具体内容
[root@CentOS7-1 ~]# expr length 123456
6
[root@CentOS7-1 ~]# expr length "hello world"
11
expr逻辑判断
语法:expr 数值1 判断符号 数值2
[root@CentOS7-1 ~]# expr 5 \> 6
0
[root@CentOS7-1 ~]# expr 8 \> 6
1
注意:进行逻辑判断时,判断符号需要用\转义
expr模式匹配
expr的两个特殊符号
- 冒号:计算字符串的字符数量
- .*:任意的字符串重复0次或者多次
执行:expr "字符串" ":" "匹配模式"
[root@CentOS7-1 ~]# expr "hello world" ":" "he"
2
[root@CentOS7-1 ~]# expr "hello world" ":" ".*o"
8
[root@CentOS7-1 ~]# expr "hello world" ":" ".*"
11
注意
- 含义是以.*的模式来匹配前面的字符串,进而计算匹配到字符的数量(必须能匹配到开头字符)
- 最后面的模式可以自定义(.*)
- 若从头匹配到多个结果,那么选取字符串长度最长的作为结果
bc命令
前言:
- bc命令是当作计算器来用的,并且是命令行的计算器
- 该命令是交互式的操作,其支持小数的计算
使用时安装bc: yum install -y bc
进入bc交互式界面:bc
通过管道符交给bc命令处理
[root@CentOS7-1 ~]# echo "1.7+5.9" | bc
7.6
[root@CentOS7-1 ~]# num=6
[root@CentOS7-1 ~]# result=`echo $num*4|bc`
[root@CentOS7-1 ~]# echo $result
24
注意:若将式子的结果交给接受变量result,那么需要将式子用反引号包裹
awk计算
前言:awk也支持小数计算
[root@CentOS7-1 ~]# echo "3.2 2.2" | awk '{print ($1+$2)}'
5.4
[root@CentOS7-1 ~]# echo "3.2 2.2" | awk '{print ($1+4*$2)}'
12
理解:awk内部本身就支持运算功能,上面的echo打印出的结果交给awk去处理,awk根据默认分隔符空格来取出第一列作为$1,取出第二列作为$2,同时计算并打印出来
中括号计算
语法:$[表达式]
#算数运算
[root@CentOS7-1 ~]# num=5
[root@CentOS7-1 ~]# res=$[num+4]
[root@CentOS7-1 ~]# echo $res
9
#逻辑判断
[root@CentOS7-1 ~]# res=$[5>9]
[root@CentOS7-1 ~]# echo $res
0
[root@CentOS7-1 ~]# res=$[8>4]
[root@CentOS7-1 ~]# echo $res
1
shell条件测试
条件测试常用语法
前言:这些命令用来评估一个表达式他的结果是真还是假
注意:
- 在条件测试中使用变量,必须加双引号
- test和[]等的作用是一样的,双中括号还支持正则处理
- 上面的尖括号应该是没有的
- 在中括号内使用数字比较符号,请添加转义符,双中括号内不需要转义符
测试表达式
对文件判断
- -e 文件名:判断在当前目录中该文件是否存在,存在为真,反之为假
- -f 文件名:判断在当前目录中该文件是否为普通文件
- -d 文件名:判断在当前目录中该字符串是否为目录类型
- -r 文件名:判断当前目录的文件是否具有查看权限
- -w 文件名:判断当前目录的文件是否具有写入权限
- -x 文件名:判断当前目录的文件是否具有可执行权限
- 文件1 -nt 文件2:判断文件1是否比文件2新
- 文件1 -ot 文件2:判断文件1是否比文件2旧
- 文件1 -ef 文件2:判断文件1和文件2是否为同一个文件
对字符串判断
- -z "字符串":判断当前字符串长度是否为0,为0则为true
- -n "字符串":判断当前字符串长度是否不为0,不为0则为true
- "字符串1" = "字符串2":若字符串1等于字符串2则为真
- "字符串1" != "字符串2":若字符串1不等于字符串2则为真
对数值变量的判断
- "数值1" -eq "数值2":判断变量两个数值是否相等
- "数值1" -ne "数值2":判断变量两个数值是否不等
- "数值1" -gt "数值2":判断变量1是否>变量2
- "数值1" -lt "数值2":判断变量1是否<变量2
- "数值1" -ge "数值2":判断变量1是否>=变量2
- "数值1" -le "数值2":判断变量1是否<=变量2
逻辑判断符号
注意:与或这两个相对于布尔运算符是具有短路功能(与:前面是错的整体就错,后面不执行;或:前面对的整体就对,后面不执行)
[root@CentOS7-1 ~]# a="my"
[root@CentOS7-1 ~]# b="you"
[root@CentOS7-1 ~]# [[ -n "$a" && ! "$a" = "$b" ]] && echo yes || echo no
yes
不同语法下符号支持区别
shell流程控制
前言:一行写多行语句,那么每行之间用分号隔开。
if语句
脚本内的写法
if 条件表达式1
then
command1
elif 条件表达式2
then
command2
else
command3
fi
命令行上的写法
if [ 条件表达式1 ];then command1;elif [ 条件表达式2 ];then echo command2;else command3;fi
注意:这里的条件表达式的测试并不一定要用[],一共有4种选择
具体案例
[root@CentOS7-1 ~]# cat other.sh
#! /bin/sh
a=10
b=20
if [ $a == $b ]
then
echo "a=b"
elif [ $a -gt $b ]
then
echo "a>b"
elif [ $a -lt $b ]
then
echo "a<b"
else
echo "没有符合的条件"
fi
[root@CentOS7-1 ~]# /bin/bash ./other.sh
a<b
case语句
前言:shell case语句为多选择语句,可以用case语句匹配一个值与一个模式,若匹配成功,则执行相匹配的命令。
脚本内写法
case 值 in
模式1)
command1
;;
模式2)
command1
;;
esac
命令行写法
case 数值 in 数值1) command1;; 数值2) command2;; *) commandn;; esac
注意:case的值可以进行数字类型匹配也可以进行字符串类型的匹配。
具体案例
[root@CentOS7-1 ~]# cat other.sh
#! /bin/sh
echo "输入1到4之间的数字"
echo "你输入的数字为:"
read num
case $num in
1) echo "1";;
2) echo "2";;
3) echo "3";;
4) echo "4";;
*) echo "你瞎输";;
esac
[root@CentOS7-1 ~]# /bin/bash ./other.sh
输入1到4之间的数字
你输入的数字为:
3
3
for循环
脚本中写法
#写法1
for var in item1 item2 …… itemn
do
command1
……
commandn
done
#写法2
for((i=0;i<=10;i=$i+1))
do
要执行的命令序列
done
命令行写法
#写法1
for num in 元素1 元素2 …… 元素n;do command命令;done
#写法2
for((i=0;i<=10;i=$i+1)); do 要执行的命令序列;done
注意:
- item1与itemn之间的一个个元素都是单独的个体,所以这些元素不能用一个引号包裹
- 每个元素之间用空格隔开
具体案例
[root@CentOS7-1 ~]# cat other.sh
#! /bin/sh
for loop in 1 2 3 4 5
do
echo "the value is $loop"
done
[root@CentOS7-1 ~]# /bin/bash ./other.sh
the value is 1
the value is 2
the value is 3
the value is 4
the value is 5
while循环
脚本中写法
while 条件表达式
do
command
done
命令行中写法
while(( 条件表达式 )); do command命令; done
具体案例
[root@CentOS7-1 ~]# cat other.sh
#! /bin/sh
int=1
while(( int <= 5 ))
do
echo $int
let "int++"
done
[root@CentOS7-1 ~]# /bin/bash ./other.sh
1
2
3
4
5
break与continue
- break:break命令允许跳出所有循环
- continue:不会跳出所有循环,仅仅跳出当前循环
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)