shell脚本(一)基础知识
shell
一、简介
1.作用:与内核进行交互
2.语言类型:解释性语言,给内核解释用
3.脚本中第一行内容:
#!/bin/bash 这是shell脚本的第一行内容,表示用bash这个解释器去解释下面的内容
#!/bin/sh 这也可以是shell脚本的第一行内容,表示用sh这个解释器去解释下面的内容
#!/usr/bin/env bash env表示使用相对路径,自动找bash解释器所在位置。可以解决不同版本的系统bash不一定在/usr/bin/bash这个路径下的问题
4.为了使脚本方便其他人阅读,一般会在开头加上作者、创建时间、邮箱、版本、描述等内容,可以使用以下设置快捷键的方法在脚本中增加这些信息vim /etc/vimrc
map <F4> ms:call WESTOS()<cr>'sfunction WESTOS()
call append(0,"######################################")
call append(1,"# Author :vaon #")
call append(2,"# Create Date:".strftime("%Y-%m-%d")." #")
call append(3,"# Email :[email protected] #")
call append(4,"# Version :1.0 #")
call append(5,"# Description: #")
call append(6,"######################################")
call append(7," ")
call append(8,"#!/usr/bin/env bash")
endfunction
#map表示影射-->按F4时调用(呼叫)WESTOS函数
#0代表第一行,1第二行...
#黄色部分strftime是一个系统自带的函数,表示显示当前时间 .是分隔符,表示这个函数与其他两段内容互不影响
#下面是设定函数的方法:
Fucton 函数名()
内容
Endfunction
然后创建一个脚本使用F4键就会在脚本最前面自动加上以下内容:二、基础知识
1.变量的声明${a}b
设定a=1
echo $a 会出现1,
但echo $ab就不会出现1b,因为系统把ab当成一个变量了。
可以echo ${a}b,会出现1b,把作为一个已定义的1
2.转义、弱引用、强引用
1)\表示转义
echo ###不会出现结果,但是加上 echo \#\#\#就会出现###
2)“”表示弱引用,也可以输出###
3)‘’表示强引用,可以输出###
4)强引用和弱引用的区别
例如 $ ! \ `` 这些强引用会把他们当作普通内容打印到屏幕上
而弱引用会把他们当成系统变量,执行变量里的内容
还例如在弱引用中`hostname`表示执行hostname命令
而在强引用中表示输出`hostname`这个内容
3.diff、patch
1)diff
【1】.
[[email protected] mnt]# cat file1
abcd
1234
[[email protected] mnt]# cat file2
abcd
[[email protected] mnt]# diff file1 file2
2d1
< 1234
#表示file1删除1234就和file2一样了
【2】.比较目录
diff -r 目录一 目录二
2)打补丁
建立hello文件:
vim hello
westos
建立hello.new文件:
vim hello.new
westos
456
安装自动更新工具包:
yum install patch -y
将hello.new与hello文件比较,多出的内容做成hello.path补丁包:
diff -u hello hello.new > hello.path
用path补丁包去更新hello文件:
patch hello hello.path
查看hello文件,已经被更新成和hello.new一样了:
cat hello
westos
456
注:如果patch后面加上-b参数表示在更新的同时备份原文件为hello.orig
首先建立一个文件:
[[email protected] mnt]# vim /mnt/passwd
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
hello
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
games:x:12:100:games:/usr/games:/sbin/nologin
test:ROOT:test
test:test:root
test:root:test
test:roottest:test
1)查找包含root子段的行
[[email protected] mnt]# grep root /mnt/passwd
root:x:0:0:root:/root:/bin/bash
test:test:root
test:root:test
test:roottest:test
2)查找包含root子段的行,不区分大小写
[[email protected] mnt]# grep -i root /mnt/passwd
root:x:0:0:root:/root:/bin/bash
test:ROOT:test
test:test:root
test:root:test
test:roottest:test
3)只要root开头的
[[email protected] mnt]# grep -i ^root /mnt/passwd
root:x:0:0:root:/root:/bin/bash
4)只要root结尾的
[[email protected] mnt]# grep -i root$ /mnt/passwdtest:test:root
5)以root开头和结尾的行都不要。-E表示后面两个条件同时筛选,-v表示不要筛选出的行
[[email protected] mnt]# grep -i root /mnt/passwd|grep -i -E "^root|root$" -vtest:ROOT:test
test:root:test
test:roottest:test
6)表示只要包含只有root子段的行,root前后只要有字母都不要,如roottest这行就不要
[[email protected] mnt]# grep -i "\<root\>" /mnt/passwd|grep -i -E "^root|root$" -vtest:ROOT:test
test:root:test
问题:
查看所有能登陆系统的用户
解答:
[[email protected] mnt]# grep "\<bash\>" /etc/passwd
root:x:0:0:root:/root:/bin/bash
可见只有root能登陆
7)grep -n 表示显示搜索出的字符串所在行号
[[email protected] mnt]# grep -n hello /mnt/passwd
4:hello
8)gep -n2 表示显示搜索出的行以及它的上面2行和下面2行,并且显示各自在文件中的行号
注:如果-n和2之间有空格则表示除了上面显示的内容还会在每行开头显示出文件名
[[email protected] mnt]# grep -n2 hello /mnt/passwd
2-bin:x:1:1:bin:/bin:/sbin/nologin
3-daemon:x:2:2:daemon:/sbin:/sbin/nologin
4:hello
5-mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
6-games:x:12:100:games:/usr/games:/sbin/nologin
9)grep -B2 表示显示搜索出的行以及这行的上两行内容
[[email protected] mnt]# grep -B2 hello /mnt/passwd
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
hello
10)grep -A2 表示下两行
[[email protected] mnt]# grep -A2 hello /mnt/passwd
hello
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
games:x:12:100:games:/usr/games:/sbin/nologin
11)表示搜索/mnt目录下包含root字符的文件
[[email protected] mnt]# grep -r hello /mnt/
/mnt/passwd:hello
5.cut
用法:cut -d 分隔符 -f 第几列 文件
-c指定字符
-d指定分隔符
-f指定第几列
cut -d : -f 2 /mnt/psswd #以:为分隔符,显示passwd文件第二列
cut -d : -f 2,7 /mnt/psswd #,表示第二和第七列,如果是-表示2到第7列
cut -c 3 /mnt/passwd #显示第三列的字符,不使用分隔符
cut -c 1,3 /mnt/passwd #显示一和三列的字符,不使用分隔符
题目:显示eth0网卡的ip
解答:
[[email protected] mnt]# ifconfig eth0|grep "\<inet\>"|cut -d " " -f 10
172.25.254.1
6.awk
-v表示给变量赋予内容:
[[email protected] mnt]# name=hello
[[email protected] mnt]# awk -F : -v name=$name 'BEGIN {print name} {print $1}END {print "end"}' /mnt/passwd
hello
root
bin
daemon
hello
mail
games
test
test
test
test
end
7.sed
-i #这个参数表示写入文件
sed ‘s/old/new/g’ passwd #将passwd文件中的所有old字符替换为new
sed ‘s/old/new/g’ passwd #将passwd文件中的所有old字符替换为new,并写入passwd文件
sed ‘2,5s/old/new/g’ passwd #替换2到5行
sed ‘/adm/,/gdm/s/old/new/g’ passwd #从adm字符一直到gdm字符的内容中将old替换为new
sed -e ‘s/old/new/g’ -e ‘s/old1/new1/g’ passwd #将old替换为new同时将old1替换为new1
sed 5d passwd #删除第5行
sed 5p passwd #复制第5行
sed -n 5p passwd #只显示第5行
-p #表示直接将 sed 的动作写在一个档案内, -f filename 则可以执行 filename 内的sed 动作
vim file
s/sbin/westos/g
s/nologin/linux/g
sed -f file passwd
题目:
将httpd服务端口改为8080
解答:
############################
# Author :vaon #
# Create Date:2017-12-16 #
# Email :[email protected] #
############################
#!/bin/bash
PORT=`grep ^Listen /etc/httpd/conf/httpd.conf`
sed "s/$PORT/Listen $1/g" -i /etc/httpd/conf/httpd.conf
systemctl restart httpd
8.设置变量
1)临时设置(环境级)
在当前环境生效,当前环境关闭,变量失效
先看一个例子:
[[email protected] mnt]# a=1
[[email protected] mnt]# echo $a
1
[[email protected] mnt]# vim test.sh
#!/bin/bash
echo $a
[[email protected] mnt]# sh test.sh
[[email protected] mnt]#
#由于a=1是在当前使用的shell里面赋的值,当进入脚本执行时,脚本使用的是子shell,这个子shell中并没有设定a=1,所以结果为空
[[email protected] mnt]# export a=1[[email protected] mnt]# sh test.sh
1
#可以临时设定a=1给所有shell,一旦登出就会失去这个临时设定
2)永久设置【1】.系统级
对所有用户生效
[[email protected] mnt]# vim /etc/profile
export a=1
[[email protected] mnt]# source /etc/profile
Connection to 172.25.254.1 closed.
[[email protected] Desktop]# ssh [email protected]
[email protected]'s password:
Last login: Sat Dec 16 09:12:16 2017 from 172.25.254.10
[[email protected] ~]# cd /mnt/
[[email protected] mnt]# sh test.sh
1
#这样所有shell都可以使用a=1这个变量,登出系统再登陆依然存在a=1
【2】.用户级
只针对配置过的用户生效,其他用户无法使用
vim /root/.bash_profile
export a=1
source /root/.bash_profile
现在a=1只对root用户生效
[[email protected] mnt]# vim /etc/profile
export PATH=$PATH:/mnt
注:这里同样存在系统级或用户级的设置,在这里使用的是系统级设置。如果只允许root可以设置/root/.bash_profile文件
[[email protected] mnt]# source /etc/profile
[[email protected] mnt]# chmod +x test.sh
然后退出/mnt目录,直接把test.sh脚本当成系统命令去执行,发现也可以:
[[email protected] mnt]# cd[[email protected] ~]# test.sh
1
10.命令别名
1)使用别名
[[email protected] ~]# vim /etc/bashrc
alias kan='cat' #表示执行'kan'的时候相当于执行'cat'命令
[[email protected] ~]# source /etc/bashrc
查看别名列表:
[[email protected] ~]# alias
...
alias kan='cat'
...
执行kan这个单词就相当于执行cat命令
[[email protected] ~]# kan /mnt/test.sh
######################################
# Author :vaon #
# Create Date:2017-12-16 #
# Email :[email protected] #
######################################
#!/bin/bash
echo $a
2)删除别名
删除/etc/bashrc中的alias kan='cat'
然后在删除kan信息:
unalias kan
11.*和@区别
vim test.sh
#!/bin/bash
echo '$1 Is' $1
echo '$2 Is' $2
echo '$3 Is' $3
echo '$* Is' $*
echo '$# Is' $#
echo '[email protected] Is' [email protected]
[[email protected] mnt]# sh test.sh 1 2 3
$1 Is 1
#显示第1个字符
$2 Is 2 #显示第1个字符
$3 Is 3 #显示第1个字符
$* Is 1 2 3 #显示所有字符
$# Is 3 #显示最后一个字符
[email protected] Is 1 2 3
#显示所有字符
*和@都表示所有内容,但实际上一个是所有内容方到“”里面。而@是每个数据都加引号
$* “1 2 3”
&@ “1” “2” “3”
12.shell脚本中函数的定义READ()
{
echo hello word
}
13.变量对比
1)test
2)[ ]
[[email protected] mnt]# a=1
[[email protected] mnt]# b=1
[[email protected] mnt]# test "$a" == "$b" && echo yes || echo no
yes
[[email protected] mnt]# [ "$a" == "$b" ] && echo yes || echo no
yes
3)[ 条件1 -a 条件2 ] 表示条件1和2都满足
4)[ 条件1 -o 条件2 ] 表示条件1和2任一个满足
5)[ 条件1 -eq 条件2 ] 等于
6)[ 条件1 -ne 条件2 ] 不等于
7)[ 条件1 -gt 条件2 ] 大于
8)[ 条件1 -ge 条件2 ] 大于等于
9)[ 条件1 -lt 条件2 ] 小于
10)[ 条件1 -le 条件2 ] 小于等于
[[email protected] mnt]# [ "$a" -eq "$b" ] && echo yes || echo no
yes
11)[ -n ] 变量内容不为空
12)[ -z ] 变量内容为空
[[email protected] mnt]# a=8
[[email protected] mnt]# [ -n "$a" ] && echo yes || echo no
yes
[[email protected] mnt]# [ -z "$a" ] && echo yes || echo no
no
13)[ -b ] 是否为块设备文件
14)[ -c ] 是否为字符设备文件
[[email protected] mnt]# [ -b /dev/sda ] && echo yes || echo no
yes
[[email protected] mnt]# [ -c /dev/vfio/vfio ] && echo yes || echo no
yes
15)[ -f ] 是否为常规文件
16)[ -S ] 是否为套接字文件
17)[ -L ] 是否为链接文件
18)[ -d ] 是否为目录
19)[ -e ] 文件是否存在
20)[ -r ] 是否可读
21)[ -w ] 是否可写
22)[ -x ] 是否可执行
18)[ filename1 -nt filename2 ] 如果 filename1 比 filename2 新,则为真
19)[ filename1 -ot filename2 ] 如果 filename1 比 filename2 旧,则为真
[[email protected] mnt]# touch file
[[email protected] mnt]# [ /mnt/check_ip -ot file ] && echo yes || echo no
yes
题目:
显示输入的文件类型
解答:
#!/bin/bash
[ -e $1 ] && (
[ -f $1 ] && echo "$1 Is File"
[ -d $1 ] && echo "$1 Is Directory"
[ -b $1 ] && echo "$1 Is Block File"
[ -L $1 ] && echo "$1 Is Link"
[ -S $1 ] && echo "$1 Is Socket"
)
14.四则运算
15.语句
case语句:
并发的去匹配所有条件,所有条件同一时间去配置,效率比if语句高
if语句是逐行去匹配,第一行不匹配再去匹配下一行,效率较低
exit 退出脚本。后面加数字1|0则表示退出后$?的值为1|0
break 退出循环