目录

什么是shell

前言

具体流程

图形化界面与命令行界面的区别

shell脚本

前言

脚本语言

脚本注释

Shebang

脚本案例

执行shell脚本的方法

父子shell

父shell

子shell

创建进程列表

含义

检测是否在子shell环境中

子shell嵌套

内置命令与外置命令

含义

内置外置命令的区分

bash基础特性

bash是什么

bash特性汇总

命令历史

调用历史命令

快捷键

命令别名

bash多命令一行执行

shell注释

shell变量

定义变量规则

变量的定义与输出

删除变量

变量的作用域

环境变量

环境变量文件位置

环境变量的命令

环境变量文件加载顺序 

特殊的shell扩展变量

declare关键字 

前言

具体案例

export关键字

使用场景 

local关键字

不使用local关键字定义变量

使用local关键字定义变量

shell字符串

不同引号的区别

单引号

双引号

反引号

区别案例 

计算字符串的长度

shell子串

内容

子串案例

shell数组

前言

数组的遍历 

具体案例

bash基础的内置命令

内置命令汇总

read命令

echo命令

eval命令

exec命令

shell参数传递

前言

参数接受处理 

具体案例

shell函数

前言

return和exit的区别

shell函数语法

函数定义

函数的调用

具体案例

函数的传参与接受参数

函数的传参

具体案例

命令行参数与函数参数的关系

shell数值计算

算数运算符

特殊符号运算

++

--

常见的算数运算符命令

(())运算

let命令运算

let实践

expr命令

expr执行加减乘除

expr求长度

expr逻辑判断

expr模式匹配

bc命令

前言:

通过管道符交给bc命令处理

awk计算

中括号计算

shell条件测试

条件测试常用语法

测试表达式

对文件判断

对字符串判断

对数值变量的判断

逻辑判断符号

不同语法下符号支持区别

shell流程控制

if语句

脚本内的写法

命令行上的写法

具体案例

case语句

脚本内写法

命令行写法

 具体案例

for循环

脚本中写法

命令行写法

具体案例

while循环

脚本中写法

命令行中写法

具体案例

break与continue

什么是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:不会跳出所有循环,仅仅跳出当前循环

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐