shell 脚本基础入门及变量使用|学习笔记

简介: 快速学习 shell 脚本基础入门及变量使用

开发者学堂课程【Linux Shell 编程入门与实战shell 脚本基础入门及变量使用】学习笔记,与课程紧密联系,让用户快速学习知识。

课程地址:https://developer.aliyun.com/learning/course/551/detail/7621


shell 脚本基础入门及变量使用


内容介绍:

一、回顾内容

二、shell 脚本示例

三、变量

四、bash 中变量的种类

 

一、回顾内容

正则表达式分为两种:基本正则表达式和扩展正则表达式

以扩展正则表达式为例,回顾用到哪些元字符

.    表示任意一个字符可以表示汉字

x*   前面字符重复任意次 (举例:写 wang* 表示 g 字母重复任意次 wanggg)

.*   任意长度的任意字符串

(wang)*  将 wang 这个字符串重复任意次 wangwangwang,扩展正则表达式不用加\,基本正则表达式写为 (wang\)*

x+   前面字符重复一次以上  xx  xxx   写法等同于 x{1, }

x{n, }   至少 n

x{m,n}  m 次到 n 次

x{,n}  最多 n 次

x{m}   精确匹配 m 次

x?     表示空,零次或一次,可有可无用这个?可以实现:默认正则表达式用到的很多都是贪婪模式,可以用?来控制只使用一次实现所谓的懒惰模式

^      表示行首

$      表示行尾

\<  或者  \b 表示单词词首

\>  或者 \b 表示单词词尾

[wang]  表示其中任意一个字符 w a n g

[^.]   表示.  注意^放在[]中表示除了……之外任意一个字符

[:alpha:]  表示字母数字用到系统自带的关键字比如字母alpha

(a|b)xy    表示 axy 或者 bxy   注意 a|bxy  表示 a 或者 bxy

(expr1)  (expr2)  如果在该行中想要调用第一个正则表达式,对应的匹配出来的字符分别对应\1  \2  注意该正则表达式必须是匹配完之后的最终结果,而不是正则表达式本身 互相引用

&   在有些命令中可以表示前面搜索出来的一些字符

比如在 vim 中用 s 搜索出来的一个字符串

输入 :%s/xyz/&er/g  意思是将 xyz 替换为 xyzer而且若一行中有多行 xyz 全部替换。如果不加 g表示默认替换一行中的第一个 xyz

vim 是基于模式的,常见的模式有三种:命令模式,插入模式,exe模式即可执行的扩展模式。此外还有替换模式、可视化模式、多窗口等模式

接上节表示 ip 地址内容

取地址 grep -o

分成0-9 10-99 100-199 200-249 250-255几段,各段之间都是或的关系,0-9用[0-9]表示,10-99用[1-9][0-9]表示,(即[1-9]中取一个数字,[0-9]中取一个数字,两个数字组合起来构成该数字),100-199用1[0-9][0-9]表示,200-249用2[0-4][0-9]表示,250-255用25[0-5]表示。数字表示完后要加.,并且用转义字符 \

ifconfig ens33| egrep -o  (([0-9] | [1-9] [0-9] | 1 [0-9] [0-9] | 2 [0-4] [0-9] | 25 [0-5])\. ) {3} ([0-9] | [1-9] [0-9] | 1 [0-9] [0-9] | 2 [0-4] [0-9] | 25 [0-5])”

可以看到取出数字

图片1.png

执行 ifconfig 看到里面存在地址更多

输入ifconfig |egrep -o “(([0-9] | [1-9] [0-9] | 1 [0-9] [0-9] | 2 [0-4] [0-9] | 25 [0-5])\. ) {3} ([0-9] | [1-9] [0-9] | 1 [0-9] [0-9] | 2 [0-4] [0-9] | 25 [0-5])

image.png

但是这种写法有缺陷,比如 2192.168.30.1002按理超出范围

echo 2192.168.30.1002  |egrep -o “(([0-9] | [1-9] [0-9] | 1 [0-9] [0-9] | 2 [0-4] [0-9] | 25 [0-5])\. ) {3} ([0-9] | [1-9] [0-9] | 1 [0-9] [0-9] | 2 [0-4] [0-9] | 25 [0-5])

结果还是能找到192.168.30.100

所以应该加入\>,如下:

echo 2192.168.30.1002  |egrep -o “\<(([0-9] | [1-9] [0-9] | 1 [0-9] [0-9] | 2 [0-4] [0-9] | 25 [0-5])\. ) {3} ([0-9] | [1-9] [0-9] | 1 [0-9] [0-9] | 2 [0-4] [0-9] | 25 [0-5])\>”

把该地址规范后显示在范围内:

echo 192.168.30.100  |egrep -o “\<(([0-9] | [1-9] [0-9] | 1 [0-9] [0-9] | 2 [0-4] [0-9] | 25 [0-5])\. ) {3} ([0-9] | [1-9] [0-9] | 1 [0-9] [0-9] | 2 [0-4] [0-9] | 25 [0-5])\>”

也可以写为带有 ?的形式:

echo 192.168.30.100  |egrep -o  ”\<(([1-9]?[0-9] |1 [0-9] [0-9] | 2 [0-4] [0-9] | 25 [0-5])\. ) {3} ([0-9] | [1-9] [0-9] | 1 [0-9] [0-9] | 2 [0-4] [0-9] | 25 [0-5])\>”

以后我们可能用正则表达式过滤一些特定字符串,思考用正则表达式表示

1.手机号 检查页面中输入的手机号是否合格

2.邮箱  检查在网站注册时邮箱格式位数不够或者形式不对

3.QQ 

4.身份证号


二、shell 脚本示例

要想编写一个脚本,该脚本要按照一定的语法书写,现在用 bash 语言来写 shell 脚本。

bash shell 脚本书写的格式要求第一行是 #!/bin/bash,后续可以加一些注释,然后按照执行顺序把现有学习过的linux命令按逻辑关系放入,就可以实现一个脚本。

脚本执行的方式有:

写绝对路径写相对路径,相对路径写.,但是这些方法都需要执行权限,否则运行不出。

那如果没有执行权限也想运行,方法用 bash,比如 bash hello.sh,或者 cat hello.sh |bash

以上都是脚本执行的方式,但是更多使用加执行权限,写路径方式。也可以把脚本放到一个对应的路径下,执行脚本时就不需要再每次写路径,只需要放到 path 变量任何一个目录下都可以,chmod +x hello.sh,

echo $PATH。

比如现在把 hello.sh 放到 root/bin 目录下,先创建 bin目录,输入 mkdir /root/bin,再将hello.sh移到文件中去,输入 mv hello.sh  /root/bin,现在运行hello.sh就不再需要写路径,输入hello.sh,显示 hello world

my hostname is centos7.magedu.com

执行脚本就像执行外部命令一样,第一次执行就按照前面学习到的搜索路径来找,一旦找到就hash到内存中,可以看到如图 /root/bin/hello.sh 记录路径

image.png

如图自动生成所有描述,但是如果编辑别的文件比如 f1 不带 sh 后缀就不会自动添加描述

vim的配置文件功能如下:

其中 autocmd BufNewFile *.sh exec “:call SetTitle()”表示如果新建一个文件是 sh 后缀,将执行 call SetTitle()语句块,SetTitle()功能块如下图,首先判断是否是sh后缀,如果是sh后缀,就在第一行加 bash,第二行加#,第三行加*****等

image.png

Shell脚本示例:

#!/bin/bash

# -----------------------------------------------

# Filename:  hello.sh

# Revision:   1.1

# Date:     2017/06/01

# Author:    wang

# Email:     wang@gmail.com

# Website:   www.magedu.com

# Description: This is the first script

# ------------------------------------------------

# Copyright:  2017 wang

# License:    GPL

echo  “hello world”

脚本调试

检测脚本中的语法错误

bash -n /path/to/some_script

shell 脚本报错有多种可能性,比如输入 hostname 命令时少写 e:

cd bin

vim f1.sh

hostnam

又继续输入

ls

rm -rf /data/*

思考 hostname 命令错误后,后续命令还会继续执行吗?

先来添加权限,输入

ll

chmod +x f1.sh

然后执行,输入

pwd

f1.sh

显示/root/bin/f1.sh: line 12: hostnam: command not found

f1.sh

所以可知 hostnam 错误并不会阻止下列命令继续执行

再来将 hostnam 错误换成 if,

再执行 f1.sh,

显示 /root/bin/f1.sh: lin 15: syntax error: unexpected end of file

所以可知后续命令未执行,if 是关键字,不能单独使用,要与其他字符一起使用,造成了语法错误,语法错误不会继续执行后面的语句,而命令错误不会阻止后续命令的继续执行。

一般在执行脚本前要先检查脚本中的错误,使用 bash -n f1.sh 就可以检测上述语法错误。除此之外,可能想要查看脚本的执行过程,就用 -x 来跟踪调试脚本过程

调试执行

bash -x /path/to/some_script

比如输入:bash -x f1.sh,显示如图

image.png

修改 hostnam 为 echo myhostname is ’hostname’,可发现在上图执行过程中 hostname 前有+,+代表命令,脚本的一些指令,现在输入bash -x f1.sh执行

image.png

发现 hostname 前有两个+,输入 cat f1.sh 查看

image.png

两个+表示 echomyhostname is ’hostname’ 命令是嵌套的,hostname是被嵌套被调用的命令,所以有两个+,两个+表示嵌套深度。

先执行

hostname 再执行外层命令


三、变量

学习中已经学到一些变量,比如带$符号的 $USER、$UID、$HOSTNAME、$PWD等,变量使用中引用符号必须加$,特定情况下要调用变量使用变量就叫引用。

不加$无法分清是命令还是字符串

变量命名的内存空间

数据存储方式︰

字符:

数值:整型,浮点型

变量:变量类型

作用∶

1、数据存储格式

2、参与的运算

3、表示的数据范围

类型:

字符

数值:整型、浮点型

例如: $UID  $HOSTNAME  $PWD  $OLDPWD  $HISTSTIZE  $PS1

NAME= “MAGE”image.png

比如画一个内存空间,其中有一个数据字符串 MAGE,通常在内存里需要有一个存放字符串的地址0101011,对于用户来讲,通过01011011地址去访问 MAGE,我们将0101011记为 NAME,NAME理解为变量,指向该地址地址,实际上是指向地址所对应的空间MAGE,通过 NAME 就可以访问字符串。 

变量名存放字符串 MAGE,在具体写指令时,就是 NAME= “MAGE”

输入 name = “mage”相当于 name 对应一块内存空间,里面存的就是 MAGE 字符串

现在想要显示name中的值,输入 echo $name

结果显示 mage   可以看到存放它的值,这就是变量的赋值及引用注意是=,而且前后不带空格。

如果想要修改 NAME 里面存放的值,不存放 MAGE,存放一个新的值 WANG,那么新值是存放在 MAGE 同一地方中还是存放在新的内存空间中呢?

存放时应该为如下形式:MAGE 还存在但是不指向 MAGE,将来系统会进行回收

image.png

输入 

name = “wang”

echo $name

显示 wang

变量的值可以来源于人为的字符串,但是比如加入这样的字符串,输入 name = “wang  xiacochun”

echo $name

显示 wang xiacochun

再比如不加 “”,输入name = wang  xiacochun

显示 bash: xiacochun: command not found...    会将空格后面的字符串当作一个命令来执行所以如果变量中有空格或者特殊符号等要加引号

除了人为的字符串也可以写入固定的命令:如果要将命令写入变量,先直接输入 name=hostname

echo $name

显示结果为hostname,只会当成字符串来处理,而不是当作命令

所以要给命令加上反引号,输入

name =‵hostname‵,将生成命令的标准输出作为 name 的值赋给它

echo $name

显示 centos7.magedu.com

当然name可以写hostname这种一行命令,name 里还可以放多行命令,输入 name =‵cat /etc/fstab‵

cat /etc/fstab

echo $name

但是显示出的是一行内容,没有保留原来分行的格式

改为  echo “$name”

就会保留原来的换行格式

image.png

除此之外,变量还可以用另一个变量赋值

输入name1 = mage

name2 = wang

name3 = $name1

echo $name3

显示 mage

若修改 name1内容为 zhangsir,输入 name1 = zhangsir

echo $name3

显示 mage

执行过程原理 name1 指向 mage ,name3 也相当于指向 mage,将 name1 改为 zhangsir后,name1 不指向 mage,但是 name3不变,还是指向 mage,所 name3还是等于 mage

若要将 name3重新赋值,输入 name3 =$name1

echo $name3

则显示 zhangsir

变量

强类型:变量不经过强制转换,它永远是这个数据类型,不允许隐式的类型转换。一般定义变量时必须指定类型、参与运算必须符合类型要求;调用未声明变量会产生错误

如java,c#

弱类型:语言的运行时会隐式做数据类型转换。无须指定类型,默认均为字符型;参与运算会自动进行隐式类型转换;变量无须事先定义可直接调用

如:  bash不支持浮点数,php

变量命名法则∶

1、不能使程序中的保留字∶例如 if, for

2、只能使用数字、字母及下划线,且不能以数字开头

3、见名知义

4、统一命名规则:驼峰命名法

 

四、bash中变量的种类

根据变量的生效范围等标准划分下面变量类型∶

局部变量︰生效范围为当前shell进程;对当前shell之外的其它shell进程,包括当前shell的子shell进程均无效

环境(全局)变量︰生效范围为当前 shell 进程及其子进程

本地变量∶生效范围为当前 shell进程中某代码片断,通常指函数

位置变量:$1,$2,...来表示,用于让脚本在脚本代码中调用通过命令行传递给它的参数

特殊变量∶$?,$0,$*,$@,$#, $$

例如:局部变量

输入name = wang

echo $name

显示的 wang 在其他终端显示不出

开启一个新的 bash 程序

输入 bash

cat /etc/passwd

image.png

事实上,root 账号一登陆就会运行一个 bash,而刚刚又再次运行了一个 bash,相当于两个 bash,而这两个 bash 进程之间是父子关系

查看父子关系:

输入 echo $PID

echo $$

显示2935,$ 表示进程的一个编号,每个程序系统会自动分配一个编号,$$ 表示当前进程的进程编号

我们可以看另一个 PPID

输入 echo $PPID    PPID 就是父进程的进程编号

显示1858

查看父子关系还可以用一个更直观的方法 pstree

输入pstree -p

显示如图,可以看到 bash(1858)——bash(2935),左侧是父进程,右侧是子进程,再往上 systemd(1) 是所有进程的父进程

image.png

再开一个 bash 进程

输入echo $$

显示编号为2987,2987是2935的子进程

 pstree 查看,可以看到多了一个子进程,并且 pstree 下开了一个进程 bash(1858)——bash(2935)——bash(2987)——pstree(3028)

思考在后面的子进程2987中可以使用前面进程里定义的变量吗?

输入 echo $$

显示2987

输入 echo $name

无显示

现在退出这个进程,相当于这个进程关闭,回到前面一级

输入 exit

显示 exit

再输入echo $PPID

显示1858

echo $$

显示2935

输入 echo $name

无显示,再退出 exit

echo $$  显示1858

输入 pstree -p

image.png

1858下不再有子进程,现在再输入 echo $name

显示 wang

注意在下级子进程中定义的变量也不会影响上级,所以只在当前 shell进程中有效

全局变量

输入 echo $$

显示1858

输入 bash

exit

再输入 bash

sleep 100

显示 ^C

输入 ls

显示 fl.sh

输入 pwd

显示 /root/bin

再输入 pstree -p,显示关系树中有 bash(1858)——bash(3171)——pstree(3202)

输入exit

exit

全局变量赋值不像局部变量一样,要加一个关键字 export

现在来定义 name = mage

echo $$  显示3215

输入 pstree -p 查看进程树关系看到进程 bash(3215)——pstree(3249)

输入 echo $name 显示 mage

想把父进程里的变量给子进程,输入 export name,意味着将普通变量变为环境变量

再输入 pstree -p 看到 bash(3215)下面没有子进程

开一个子进程 bash

输入 echo $$  显示进程编号为3266

输入 pstree -p 看到进程 bash(3215)——bash(3266)

现在显示一下刚才 name 的变量

输入 echo $name

显示 mage

也能修改当前内容  echo $$

显示3266

输入 name = wang

echo $name

显示 wang

如果再退出回到上级进程 输入 exit

echo $name

显示 mage

所以全局变量的特点是父进程的变量可以传给子进程,但是子进程变量若修改不影响父进程

若想删除变量,输入 unset name

echo $name   值为空

局部变量总结

变量赋值:name = ’value’

可以使用引用 value:

(1) 可以是直接字符name= “root”

(2) 变量引用:name = “$USER”

(3) 命令引用:name = ‵COMMAND‵ name= $(COMMAND)

变量引用: ${name}  $name

“” :弱引用,其中的变量引用会被替换为变量值

‘’ : 强引用,其中的变量引用不会被替换为变量值,而保持原字符串

显示已定义的所有变量:set

删除变量:unset name

相关文章
|
4天前
|
弹性计算 运维 监控
|
5天前
|
存储 弹性计算 运维
自动化收集员工信息的Shell脚本
【4月更文挑战第30天】
6 0
|
6天前
|
弹性计算 运维 Shell
使用shell 脚本打印图形3
【4月更文挑战第29天】
13 0
|
6天前
|
存储 弹性计算 运维
使用shell 脚本打印图形2
【4月更文挑战第29天】
13 0
|
6天前
|
弹性计算 运维 Shell
使用shell 脚本打印图形1
【4月更文挑战第29天】
14 0
|
6天前
|
存储 弹性计算 运维
调整虚拟机内存参数的shell 脚本
【4月更文挑战第29天】
11 0
|
6天前
|
弹性计算 运维 Shell
一键申请多个证书 shell 脚本
【4月更文挑战第29天】
11 1
|
6天前
|
弹性计算 运维 Shell
从shell脚本发送邮件
【4月更文挑战第29天】
11 0
|
6天前
|
弹性计算 运维 Shell
使用 shell 脚本打印图形
【4月更文挑战第29天】
13 1
|
6天前
|
弹性计算 运维 Shell