shell脚本基础
知识要点
掌握Shell脚本的基础知识
学会使用Shell变量
学会编写简单的Shell脚本
Shell脚本的应用环境
学习shell脚本的基本流程
看、想、写
shell脚本用在什么地方
编写常用系统维护工具菜单
重要的性能参数、进程和日志分析
自动实现数据备份计划
自动批量搭建特定系统环境
防火墙自动配置脚本
服务器的配置文件安全比对
对批量设备进行远程巡检
Shell脚本的组成元素
shell脚本的基本组成
- 声明和注释*
- 系统命令
- 文本处理工具(grep、cut、sed、awk…)
- 各种变量
- 各种条件判断
- 循环结构语句
- 各种函数
Shell的作用
Shell的作用 —— 命令解释器,“翻译官”
介于系统内核与用户之间,负责解释命令行
Shell的种类和切换
登录Shell
- 指用户每次登录系统后自动加载的Shell程序,大多数Linux系统采用 /bin/bash 作为默认登录Shell
- /etc/shells 文件记录了系统支持的有效登录Shell
如何切换Shell环境
- 临时切换:直接执行其他Shell程序,例如ksh、zsh等
- 返回到原来的Shell环境时可以执行“exit”命令或者按Ctrl+D快捷键
- 更改用户登录Shell:
- 需修改 /etc/passwd 文件中用户记录的最后一个字段
- 或执行:usermod -s Shell程序路径 用户名
[root@localhost ~]# cat /etc/shells //查看有哪些shell
/bin/sh
/bin/bash
/sbin/nologin
/bin/tcsh
/bin/csh
/bin/ksh
Shell初始化
bash初始化
- 登录shell
指的是输入用户名、密码, 从系统登录时执行的第一个程序(自动启动)
- 非登录shell(手工启动)
登录系统后, 在login shell里启动的shell是非login shell
如执行bash命令、在图形中打开终端均是开一个非登录shell
bash启动时,会进行初始化,初始化就是执行一些脚本,有哪些脚本呢?
分2种情况:
- 第1种:登录时启动的bash,登录shell
初始化脚本执行顺序:/etc/profile->/etc/profile.d/*.sh->~/.bash_profile->~/.bashrc->/etc/bashrc
如果这些脚本中的变量发出冲突,那么以最后一个脚本的设置生效。
- 第2种:登录后,启动的bash,非登录shell
初始化脚本执行顺序:~/.bashrc->/etc/bashrc->/etc/profile.d/*.sh
初始化主要是定义一些变量、别名和函数
Shell启动配置文件的区别
(环境变量)
/etc/profile:配置全局环境变量,影响所有用户
~/.bash_profile :配置个人环境,影响一个用户
(函数,别名)
/etc/bashrc :配置全局的别名或者shell选项,影响所有用户
~/.bashrc :配置个人别名或者shell选项,影响一个用户
登录和非登录Shell区别
登录Shell负责系统全局环境(env)初始化,会读取所有启动配置文件
非登录Shell默认会继承登录shell的环境变量,为了加快速度,无需读取所有启动配置文件,只需读取少量局部配置文件
退出登录Shell:~/.bash_logout
Bash的命令历史
命令历史
保存用户曾经执行过的命令操作
存放位置:~/.bash_history 文件
查看历史命令
使用↑、↓按键逐条翻看,允许编辑并重复执行
执行:history
清除历史命令
执行:history -c
[root@localhost root]# history
……
556 useradd jerry
557 passwd jerry
558 crontab -e -u jerry
559 crontab -l -u jerry
调用历史命令
!n:执行历史记录中的第n条命令
!str:执行历史记录中以“str”开头的命令
设置记录历史命令的条数
修改 HISTSIZE 参数(默认为1000条)
[root@localhost root]# !562
crontab -l -u jerry
no crontab for jerry
[root@localhost ~]# vi /etc/profile
HISTSIZE=200
Bash的命令别名
命令别名
为使用频率较高的复杂命令行设置简短的调用名称
存放位置:~/.bashrc
查看命令别名
格式:alias [别名]
设置命令别名
执行:alias 别名=\'实际执行的命令\'
取消已设置的命令别名
格式:unalias 别名
unalias -a
[root@localhost ~]# alias
alias cp=\'cp -i\'
alias l.=\'ls -d .* --color=tty\'
alias ll=\'ls -l --color=tty\'
alias ls=\'ls --color=tty\'
alias mv=\'mv -i\'
alias rm=\'rm -i\'
……
编写第一个Shell脚本
编写脚本代码
从上往下,按行执行,错误跳过,继续执行
- 使用vi文本编辑器
- 每行一条Linux命令,按执行顺序依次编写
#!/bin/bash
#这是我的第一个脚本:打印Hello world
#脚本版本
#作者
#日期
str="hello world"
echo ${str}
////手工执行
[root@localhost ~]# cd /opt/scripts
[root@localhost scripts]# bash first_script.sh
hello world
赋予可执行权限
- 使脚本具有可执行属性
[root@localhost ~]# chmod +x first_script.sh
[root@localhost ~]# ls -l first_script.sh
-rwxr-xr-x 1 root root 144 04-26 15:02 first_script.sh
[root@localhost scripts]# ./first_script.sh
hello world
[root@localhost scripts]# /opt/scripts/first_script.sh
hello world
执行脚本文件
- 方法一:脚本文件路径(要求脚本有x权限)
- 方法二:bash 脚本文件路径(要求脚本有x权限)
- 方法三:source 或者. 脚本文件路径(不要求脚本有x权限)
[root@localhost ~]# ./first.sh // 必须有 x 权限
/boot
-rw-r--r-- 1 root root 1.8M 2010-03-17 vmlinuz-2.6.18-194.el5
[root@localhost ~]# bash /first.sh // 必须有 x 权限
/boot
-rw-r--r-- 1 root root 1.8M 2010-03-17 vmlinuz-2.6.18-194.el5
[root@localhost ~]# source /first.sh //不要求 x 权限
/boot
-rw-r--r-- 1 root root 1.8M 2010-03-17 vmlinuz-2.6.18-194.el5
执行脚本方法的区别
- 区别1:
方法一、二:要求脚本有x权限
方法三:不要求脚本有x权限
- 区别2:
方法一、二:会产生新的bash进程,有新的bash进程来解析脚本中的代码
方法三:不会产生新的bash进程,会使用当前的bash进程来解析脚本中的代码
脚本结构
- 声明解释器(声明)
#!/bin/bash
- 注释信息
#这是我的第一个脚本:打印Hello world
#脚本版本
#作者
#日期
- 定义变量、函数
- 实现功能
[root@localhost ~] vi /first.sh
#!/bin/bash
# This is my first Shell-Script.
cd /boot
echo "当前的目录位于:"
pwd
echo "其中以vml开头的文件包括:"
ls -lh vml*
练习:
写脚本:
实现修改root用户的密码
要求:
修改密钱,提示“准备修改root的密码”
修改密码,不要有任何的输出
修改后,提示“修改root的密码完成”
Shell变量的作用、类型
变量的作用
- 为灵活管理Linux系统提供特定参数,有两层意思:
- 变量名:使用固定的名称,由系统预设或用户定义
- 变量值:能够根据用户设置、系统环境的变化而变化
变量的类型
- 自定义变量:由用户自己定义、修改和使用
- 环境变量:由系统维护,用于设置工作环境
- 位置变量:通过命令行给脚本程序传递参数
- 预定义变量:Bash中内置的一类变量,不能直接修改
按变量使用的范围
- 局部变量:只在当前的shell中有效
- 全局变量:子shell会从父shell继承的变量
自定义变量
定义新的变量
- 变量名要求以英文字母或下划线开头,不能以数字开头,区分大小写,约定俗成为大写,但不强制
- 等号两边不要空格
- 格式:变量名=变量值(有空格: “asdsadsad a”,空格是命令行的分隔符)
查看变量的值
- 格式:echo $变量名
[root@localhost ~]# DAY=Sunday
[root@localhost ~]# echo $DAY //通过$符号引用指定名称的变量值
Sunday
[root@localhost ~]# DAY=“Today is Sunday”//变量值有空格用双引号括起来
[root@localhost ~]# echo $DAY
Today is Sunday
引用变量:
如果变量名容易和后边的字目和下划线连在一起导致混淆,则应该使用大括号将变量名括起来
${变量名}
//
[root@localhost ~]# DAY=Sunday
[root@localhost ~]# echo “Today is Sunday ” > $DAY_file.txt
[root@localhost ~]# ls -a
. .. .txt
[root@localhost ~]# echo “Today is Sunday ” > ${DAY}_file.txt
[root@localhost ~]# ls -a
. .. Sunday_file.txt
可以将命令的执行结果直接赋值给变量
var2=$( rpm -qf $(which fdisk) )
readonly可将变量设置为只读,变量一旦设置为只读,任何用户不能对此变量进行重新赋值
- variable=value #先对一个变量进行赋值
- readonly variable #将variable设为只读
利用unset命令可以清除变量的值
- 格式:unset 变量名
shell命令行替换
bash shell 在解释命令前替换某些命令行元字符
- 统配符替换:*、?、[a-z]
- 历史命令替换:!!、!n、!str
- 代字号替换:~、~用户名
- 变量替换:$变量名、${变量名}
- 大括号替换:{a,b,c}file、{aa,bb}/{aa,bb}
- 算术替换:+、-、*、/
shell中引号
引用和转义字符
- 使用特殊字符时,就是表示本身,不使用其特殊意义
- \ :避免下一个字符被shell解释
- \\:表示 \
- 双引号 “ ”:避免双引号内除了$、!和`(反引号)以外的其它字符被shell解释
(没有内外层,就近)
- 单引号 ‘ ’:避免单引号内的任何字符被shell解释(没有内外层,就近)
- 反引号 ``:命令替换,提取命令执行后的输出结果(没有内外层,就近)
- 单引号、双引号、反引号的区别
shell替换发生在命令运行之前
echo命令
在屏幕显示字符串
echo命令的-e选项表示将转义符\后跟字符形成的特殊字符解释成特殊意义
[root@localhost scripts]# echo -e "123\n123"
123
123
[root@localhost scripts]# echo - "123\n123"
- 123\n123
自定义变量
read命令从键盘输入内容为变量赋值
- 格式: read [选项] 变量名
- -p:提示信息
- -s:隐藏输入
- -t:指定超时时间
- -n:指定读取的长度
#!/bin/bash
read -p "please input you name: " name
read -n3 -p "input password: " pass
echo -e "\n your username is $name password is $pass"
设置变量的作用范围export
- 格式1:export 变量名 ...
- 格式2: export 变量名=变量值 ...
- —— 两种格式可以混合使用
- export 查看全局变量
[root@localhost ~]# echo $FILESVR
filesvr.sxjy.com
[root@localhost ~]# export FILESVR //导出为全局变量
[root@localhost ~]# bash
[root@localhost]~# echo $FILESVR //子程序引用全局变量
filesvr.sxjy.com
全局变量
查看全局变量
- set命令可以查看所有的Shell变量,其中包括全局变量
- env命令只查看全局变量
[root@localhost root]# set
……
SHELL=/bin/bash
TERM=xterm
UID=0
USER=root
consoletype=pty
环境变量
环境变量
- 由系统提前创建,用来设置用户的工作环境
- 配置文件: /etc/profile、~/.bash_profile
常见的环境变量:
- PWD、PATH
- USER 、LOGNAME、UID、PPID、SHELL、HOME
- PS1、$PS2
[root@localhost ~]# echo $PATH
/usr/kerberos/sbin:/usr/kerberos/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin
[root@localhost ~]# PATH="$PATH:/root"
[root@localhost ~]# echo $PATH
/usr/kerberos/sbin:/usr/kerberos/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin:/root
间接引用变量
二次引用(间接取值)
[root@yuelu0324 tmp]# a=b
[root@yuelu0324 tmp]# b=1
[root@yuelu0324 tmp]# echo ${${a}}
-bash: ${${a}}: bad substitution
[root@yuelu0324 tmp]# echo ${$a}
-bash: ${$a}: bad substitution
[root@yuelu0324 tmp]# echo ${!a}
1
${}
位置变量
位置变量
- 表示为 $n,n为1~9之间的数字
- 大于9的位置参数要用{},例如${10},$0表示叫脚本本身
预定义变量
预定义变量
- $#:命令行中位置变量的个数
- $*:所有位置变量的内容(较少使用)
- $@:所有位置变量的内容
- $0:当前执行的进程/程序名
- $$ :当前shell的PID值, echo $$; ps $$, 常用作临时变量的后缀
- $?:上一条命令执行后返回的状态,当返回状态值为0时表示执行正常,非0值表示执行异常或出错
- $RANDOM :随机数,可以作为临时文件名
touch abc$RANDOM.txt
位置变量示例
对比$*和$@的区别
返回值判断
有条件运行多个命令
- cmd1 && cmd2
- cmd1成功了(返回值为0)才会运行cmd2
- cmd1 || cmd2
- cmd1失败了(返回值为非0)才会运行cmd2
[root@localhost ~]# rpm -q bind &>/dev/null && echo ok || echo no
no
[root@localhost ~]# rpm -q setup &>/dev/null && echo ok || echo no
ok
备份脚本使用位置变量和预定义变量
[root@localhost ~]# cat bak.sh
#!/bin/bash
TARFILE=bak-`date +%s`.tgz //Unix的时间戳,从1970-1-1 0:0:0 到某个时间的秒数
tar zcf $TARFILE $@ &> /dev/null
echo "已执行 $0 脚本,"
echo “共完成 $# 个对象的备份"
echo “具体包括: $*”
[root@localhost ~]# ./bak.sh /etc/passwd /etc/shadow
已执行 ./bak.sh 脚本,
共完成 2 个对象的备份
具体包括:/etc/passwd /etc/shadow
Bash的重定向操作
改变标准输入、标准输出、标准错误的方向
Here Documet用法
传递命令序列到程序
[root@localhost ~]# lftp 10.10.10.1 << EOF
> ls pub
> get pub/passwd
> quit
> EOF //后面一定不要有空格
-rw-r--r-- 1 0 0 1759 Jul 11 06:00 passwd
[root@localhost ~]# cat << EOF > test.sh
> #!/bin/bash
> echo "this is here document test"
> EOF
[root@localhost ~]# cat test.sh
#!/bin/bash
echo "this is here document test"
Here Documet示例
编写脚本自动创建mysql数据库,创建完毕显示结果
#!/bin/bash
read -p "请输入要创建数据库的名称:" name
mysql -u root -p123 << EOF
create database $name;
show databases;
EOF
[root@localhost test]# bash test.sh
请输入要创建数据库的名称:sxjy
Database
information_schema
cacti
mysql
sxjy
案例分析
实验案例1
编写脚本显示如下图所示效果,分析显示的结果
name="hello"
myname0=\'My name is $name\'
myname1="My name is \'$name\'"
myname2=\'My name is "$name"\'
myname3="My $name is "$name""
myname4=\'My $name is \'$name\'\'
echo $myname0
echo $myname1
echo $myname2
echo $myname3
echo $myname4
实验案例2
编写脚本显示如下图所示效果,要求选择一个菜单后,不用按回车马上显示出结果
***系统管理工具***
1. 显示磁盘空间信息
2. 显示网络接口信息
3. 显示内存使用信息
0. 退出菜单
输入选项: 3
你的选择是 3
实验案例3
编写脚本,根据输入的用户名和密码(密码不能显示出来)自动新建用户并配置密码,脚本运行如下图所示(不能显示多余的提示)
add new user: txy
user txy password:
user txy is ok
实验案例4
编写脚本实现下列功能
- 脚本的功能是统计某个文件夹下有多少个文件夹,包括子文件夹和隐藏文件夹,注意不要把查找目录本身.和..算进来
- 脚本需要接一个参数就是文件夹的绝对路径位置
- 运行完毕显示“The number of directory: 数目”
[root@localhost ~]# ./counter.sh /usr
The number of directory: 7511
[root@localhost ~]# ./counter.sh /etc
The number of directory: 257
[root@localhost ~]# ./counter.sh /root
The number of directory: 169
实验案例6
编写脚本实现下列功能
- 脚本的功能是统计某个文件夹下,某种文件后缀名的文件的数目
- 脚本的参数就是接需要查找的文件的后缀名,可以有多个参数
- 目录路径和文件名的后缀,通过位置参数传入
[root@localhost test1]# ./test.sh /etc conf log
以conf结尾的文件的数目是: 311
以log结尾的文件的数目是: 6
实验案例7
- 利用Here Document创建本地YUM源配置文件
- 通过参数指定光盘挂载点
- 挂载点如果不存在需要创建
- 备份/etc/yum.repos.d/目录下的其它配置文件
- 最后显示yum is ok,不能显示多余的正确或错误提示