因为shebang机构,所以shell脚本格式要求如下:首先vim HELLO.sh
#!/bin/bash
#
#echo "hello word"
bash执行
[root@localhost ~]#
bash HELLO.sh
hello word
新建PATH文件路径
[root@localhost ~]#
vim /etc/profile.d/newfile.sh
[root@localhost ~]#vim 新的文件.sh
#在新文件.sh文件里编辑:PATH=/data/新的目录/:$PATH
#记得chmod +x 文件名.sh,加权限噢
语法检查
bash -n filename.sh
调试执行
bash -x filename.sh
作用
1.数据存储方式
2.参与的运算
3.表示的数据范围
shell中变量命名法则
1.不能以程序中的保留字.例如:if.for
2.只能使用数字、字母及下划线、且不能以数字开头
3.见名知意
4.统一命名规则:驼峰命名法即f分大驼峰:FileName;小驼峰:fileName
shell中命名建议规则
1.变量名大写
2.局部变量小写
3.函数名小写
4.用英文名,并体现出实际作用
赋值
可直接是字符串:
1.name=sunxiaobo
2.name=“sunxiaobo”
变量的引用:
name="$PATHNAME"
命令的引用:
1.name=COMMAND
2.name=$((COMMAND))
引用
1.echo $name
2.echo ${name}
#set可以显示已定义的所有变量
#unset PATHNAME可用于删除变量的值(常用于脚本最后)
[root@localhost ~]#unset name
[root@localhost ~]#echo $name
[root@localhost ~]#
强弱引用
""弱引用,其中的变量引用会被替换为变量值
''强引用,其中的变量引用不会被替换为变量值而保持原字符串
相关小命令
echo $BASHPID #查看对应进程编号
echo $PPID #查看父进程编号
1.当前shell生效
2.无法被子进程继承
1.定义环境变量:
ecport name=PATHNAME
declare -x name=PATHNAME
2.查看所有环境变量:export、env、pritenv、declare -x
函数中的变量
变量 | 用途 |
---|---|
$NUM | 第NUM个参数 |
$0 | 命令本身 |
$* | 传递给脚本的所有参数,所有参数合一字符串 |
$@ | 传递给脚本的所有参数,每个参数为独立字符串 |
$# | 传递给脚本的参数的个数 |
$? | 执行成功与否(0成功)(1-255失败) |
注意: @ 、 @、 @、*只在被双引号包起来的时候才会有差异
readonly又称常量
面试题:
[root@localhost ~]#TITLE=ceo
[root@localhost ~]#echo $TITLE
ceo
[root@localhost ~]#(echo $TITLE;TITLE=COO;echo $TITLE)
ceo
coo
[root@localhost ~]#echo $TITLE
ceo
注意:
{}不开启子进程
()开启子进程
#内置命令和变量赋值只影响小括号里的变量环境,不影响小括号外面的变量环境。
+、-、*、%(取模、取余)、**(乘方)
#乘除符号有些场景需要转义
1.let var =算术运算
2.var = $[ 算术运算 ]
3.var = $(( 算术运算 ))
4.var = $(expr NUM1 NUM2)
5.declare -i var = 数值
6.echo " 算术运算 " | bc
$RANDOM
例如:echo [ [ [RANDOM%50]
1.条件测试语法
-eq | 相等 |
-ne | 不相等 |
-lt | 小于 |
-le | 小于等于 |
-gt | 大于 |
-ge | 大于等于 |
真true:0
假false:1
2.测试命令
test 表达式
[ “表达式” ]
[[ “表达式” ]]
短路真/短路假
CMD1 && CMD2 || CMD3
CMD1结果返还true执行CMD2;CMD1结果返还false执行CMD3.
字符 | 用途 |
---|---|
= | 是否等于 |
> | 是否大于(ascii码)) |
< | 是否小于 |
!= | 是否不等 |
== | 使用通配符与右边匹配 |
=~ | 使用正则表达式与右边匹配,一般用于[[]]中 |
-z “STRING” | 字符串是否为空,空为真,不空为假 |
-n “STRING” | 字符串是否不为空,空为假,不空为真 |
文件类型存在测试
语法 | 用途 |
---|---|
-a FILE | 同-e |
-e FILE | 文件是否存在 |
-b FILE | 文件是否存在(块设备文件) |
-c FILE | 文件是否存在(字符文件) |
-d FILE | 目录是否存在 |
-f FILE | 文件是否存在(普通文件) |
-h FILE 或 -L FILE | 是否纹在符号链接文件 |
-p FILE | 是否存在管道文件 |
-S FILE | 是否存在套接字文件 |
文件权限测试
语法 | 用途 |
---|---|
-r FILE | 可读 |
-w FILE | 可写 |
-x FILE | 可执行 |
-u FILE | 是否suid权限 |
-g FILE | 是否sgid权限 |
-k FILE | 是否sticky权限 |
文件属性测试
语法 | 用途 |
---|---|
FILE1 -ef FILE2 | FILE是否为FILE2的硬链接 |
FILE1 -nt FILE2 | FILE是否新于FILE2(mtime) |
FIEL1 -ot FILE2 | FILE是否旧于FILE2 |
bash的组合测试条件
第一种方式:
COMMAND1 && COMMAND2 并且
COMMAND1 || COMMAND2 或者
!COMMAND 非
第二种方式:
COMMAND1 -a COMMAND2 并且;短路与
COMMAND1 -o COMMAND2 或者;短路或
!COMMAND 非
read [OPTION] VAR…
#使用read来把输入值分配给一个或多个变量
-p STRING #指定要显示的提示
-s #静默输入,一般用于密码
-n NUM #指定输入的字符长度NUM
-d CHAR #指定结束符CHAR
-t NUM #指定时间为NUM
#read 从标准输入中读取值,给每个单词分配一个变量,所剩词条多被分配给最一个变量
一次对多个变量赋值
[root@localhost ~]#read A B C <<< "aa bb cc"
[root@localhost ~]#echo $A
aa
[root@localhost ~]#echo $B
bb
[root@localhost ~]#echo $C
cc
从管道获得值
[root@localhost ~]#echo sun | { read NAME;echo $NAME; }
sun
if语句
单分支:
if CONDITION(判断条件); then
CODE(条件为真的分支代码)
else
CODE(条件为假的分支代码)
fi
嵌套式:
if CONDITION(判断条件1);then
CODE(条件1为真的分支代码)
elif CONDITION(判断条件2);then
CODE(条件2为真的分支代码)
else
CODE(条件2为假的分支代码)
fi
case语句
case $VAR(变量引用) in
PAT1)
CODE(满足条件1的分支代码)
;;
PAT2)
CODE(满足条件2的分支代码)
;;
*)
CODE(都不满足条件的分支代码)
;;
esac
case语句支持的glob风格的通配符
*: 任意长度字符
?: 任意单个字符
[]: 指定范围内的任意单个字符
a|b: a或b
bash如何展开命令
把命令行分成单个命令词
展开别名
展开大括号的申明({})
展开波浪符声明(~)
命令替换$() 和``)
再次把命令行分成命令词
展开文件通配(*、?、[abc]等等) 准备I/O重导向(<>)
运行程序
防止扩展
反斜线()会使随后的字符按原意解释
$echo Your cost:\$5.00
Your cost:$5.00
加引号来防止扩展
单引号(')防止所有扩展
双引号(")也防止所有扩展,但是以下情况例外:
$(美元符号) - 变量扩展
`(反引号) - 命令替换
\(反斜线) - 禁止单个字符扩展
括号的申明({})
展开波浪符声明(~)
命令替换$() 和``)
再次把命令行分成命令词
展开文件通配(*、?、[abc]等等) 准备I/O重导向(<>)
运行程序
防止扩展
反斜线(\)会使随后的字符按原意解释
$echo Your cost:\$5.00
Your cost:$5.00
加引号来防止扩展
单引号(')防止所有扩展
双引号(")也防止所有扩展,但是以下情况例外:
$(美元符号) - 变量扩展
`(反引号) - 命令替换
\(反斜线) - 禁止单个字符扩展
!(叹号) - 历史命令扩展
编写脚本 systeminfo.sh,显示当前主机系统信息,包括主机名,IPv4地址,操作系统版本,内核版本,CPU型号,内存大小,硬盘大小。
#!/bin/bash
IPV4=`ifconfig|grep -Eo "\b([1-9]|[1-9][0-9]|1[0-9]{2}|2[01][0-9]|22[0-3])\b(\.\b([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\b){2}\.\b([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\b"`
echo "当前主机系统信息如下:"
echo 主机名:`hostname`
echo ipv4地址:"$IPV4"
echo 操作系统版本:`cat /etc/redhat-release|cut -d. -f1-2`
echo 内核版本:`uname -r`
echo CPU型号:`lscpu|grep "Model name"|tail -1|tr -s ' '|cut -d: -f2`
echo 内存大小:`free -mh|head -2|tail -1|tr -s ' '|cut -d' ' -f2`
echo 硬盘容量:`fdisk -l|head -2|tail -1|cut -d, -f1|cut -d' ' -f2-4`
编写脚本 backup.sh,可实现每日将/etc/目录备份到/backup/etcYYYY-mm-dd中。
#!/bin/bash
cp -a /etc/ /data/etc`date +%Y-%m-%d` &> /dev/null
echo "/etc目录备份完成
编写脚本 disk.sh,显示当前硬盘分区中空间利用率最大的值。
#!/bin/bash
echo "当前硬盘分区中空间利用率最大值:`df|grep /dev/sd|grep -o "[0-9]\{1,3\}%"|sort -nr|head -1`"
编写脚本 links.sh,显示正连接本主机的每个远程主机的IPv4地址和连接数,并按连接数从大到小排序。
#!/bin/bash
echo "每个远程主机的IPV4地址和连接数:`netstat -tan|tr -s " " ":"|cut -d: -f6|grep ^[[:digit:]]|sort|uniq -c|sort -nr`"
编写脚本 sumspace.sh,传递两个文件路径作为参数给脚本,计算这两个文件中所有空白行之和。
#!/bin/bash
space1=`cat $1|grep "^$"|wc -l`
space2=`cat $2|grep "^$"|wc -l`
let sum=space1+space2
编写脚本 sumfile.sh,统计/etc, /var, /usr 目录中共有多少个一级子目录和文件。
#!/bin/bash
numetc=`ls -A /etc|wc -l`
nuvar=`ls -A /var|wc -l`
numusr=`ls -A /usr|wc -l`
let sum=numetc+numvar+numusr
echo $sum
编写脚本 argsnum.sh,接受一个文件路径作为参数;如果参数个数小于1,则提示用户“至少应该给一个参数”,并立即退出;如果参数个数不小于1,则显示第一个参数所指向的文件中的空白行数。
#!/bin/bash
[[ $# -lt 1 ]] && ( echo "at least one argument" && exit ) || echo `grep "^$" $1|wc -l`
编写脚本 hostping.sh,接受一个主机的IPv4地址做为参数,测试是否可连通。如果能ping通,则提示用户“该IP地址可访问”;如果不可ping通,则提示用户“该IP地址不可访问”。
#!/bin/bash
ping -c1 -w1 $1 &> /dev/null && echo "$1 is up"||echo "$1 is down"
编写脚本 checkdisk.sh,检查磁盘分区空间和inode使用率,如果超过80%,就发广播警告空间将满。
#!/bin/bash
disk=`df|egrep /dev/sd|tr -s ' ' '%'|cut -d% -f5|sort -nr|head -n1`
inode=`df -i|egrep /dev/sd|tr -s ' ' '%'|cut -d% -f5|sort -nr|head -n1`
[ $disk -ge 80 -o $inode -ge 80 ] && echo wall space will full
编写脚本 per.sh,判断当前用户对指定参数文件,是否不可读并且不可写。
#!/bin/bash
[ ! -r "$1" -a ! -w "$1" ] && echo "$1 is not read an write" || exit
#注意:执行时请以一般用户执行,以root执行无法显示结果
编写脚本 excute.sh ,判断参数文件是否为sh后缀的普通文件,如果是,添加所有人可执行权限,否则提示用户非脚本文件。
#!/bin/bash
[[ $1 =~ .sh$ ]] && (chmod a+x $1;echo "$1 is .sh")||echo "$1 is not .sh"
编写脚本 nologin.sh和 login.sh,实现禁止和允许普通用户登录系统。
#!/bin/bash
[ -f /etc/nologin ]&& echo "nologin"||(touch /etc/nologin;echo "nologin")
#!/bin/bash
[ -f /etc/nologin ]&& (rm -f /etc/nologin;echo "login")||echo "login"
编写脚本 createuser.sh,实现如下功能:使用一个用户名做为参数,如果指定参数的用户存在,就显示其存在,否则添加之;显示添加的用户的id号等信息。
#!/bin/bash
read -p "please input username:" user
id $user &>/dev/null
[[ ! $? -eq 0 ]]&&(useradd $user&> /dev/null && echo "add $user user") ||echo "the user is exit"
编写脚本 yesorno.sh,提示用户输入yes或no,并判断用户输入的是yes还是no,或是其它信息。
#!/bin/bash
read -p "Do you agree?(yes or no):" ANS
[[ "$ANS" =~ ^[Yy][Ee][Ss]|[Yy]$ ]] && { echo OK ; exit ; }
[[ "$ANS" =~ ^[Nn][Oo]|[Nn]$ ]] && { echo Not OK ; exit ; }
echo "Your input is false"
编写脚本 filetype.sh,判断用户输入文件路径,显示其文件类型(普通,目录,链接,其它文件类型)
#!/bin/bash
read -p "please input file path: " path
a=`ls -l $path|grep -o "^."`
case $a in
-)
echo "common file"
;;
d)
echo "directory file"
;;
1)
echo "linked file"
;;
*)
echo "other file"
;;
esac
if [ -z "$1" ];then
echo there must be a filename argument
exit 1
elif [ ! -e "$1" ]; then
echo no such file
exit 1
elif [ -d "$1" ]; then
echo directory file
exit 0
elif [ -L "$1" ]; then
echo link file
exit 0
elif [ -f "$1" ]; then
echo normal file
exit 0
else
echo other file
exit 0
fi
编写脚本 checkint.sh,判断用户输入的参数是否为正整数。
#!/bin/bash
read -p "please input a number:" NUM
[[ $NUM =~ ^[0-9]+$ ]] && echo $NUM is int ||{ echo "please input a number"; exit ; }