文章目录
BASH基础
Shell为Linux提供了编程环境
程序 = 指令 + 数据
编程风格:
过程式:以指令为中心,数据服务于命令
对象式:以数据为中心,命令服务于数据
shell是一种过程式编程
过程式编程:
顺序执行
循环执行
选择执行
如何判断shell脚本: 文件中头行: #!/bin/bash 标识
运行脚本方法:
给文件给予可执行权限,通过具体的文件路径执行文件执行
chmod +x xxx.sh
./path/to/xxx.sh
直接运行解释器,将脚本作为解释器程序的参数运行
bash xx.sh
[root@node1 ~]# echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/
系统运行命令寻找的路径,如果你将脚本文件放入到以上路径下,可以将脚本直接作为命令去执行。
变量
变量命名:
命名只能使用英文字母,数字,下划线;首字母不能以数字开头
中间不能够有空格,可以使用_下换线
不能够使用标点符号(特殊字符)
不能够使用bash中的关键字
有效的命名:
RUNshell
L_inux
var1
无效的命名:
?var1
user*name
赋值
将语句给变量赋值
for file in “ls /etc/”
for file in $(ls /etc/)
[root@node1 ~]# for file in "ls /tmp/";do echo $file;done
将文件路径/tmp/下的所有文件赋给变量file,do…;done之间是循环体。
该条命令是循环显示/tmp/下的文件名。
使用变量:
定义变量: varname=“value”
使用变量: echo $varname | echo ${varname}
建议:使用{}作为边界
只读变量:
readonly关键字可以将变量作为只读变量,变量无法被修改
[root@node1 ~]# vim 1.sh
在里面写入
#!/bin/bash
url="www.baidu.com"
echo ${url}
url="www.google.com"
echo ${url}
把"www.baidu.com"赋值给变量url,使用变量
再把"www.google.com"赋值给变量url,使用变量
然后给1.sh执行权限
[root@node1 ~]# chmod +x 1.sh
接下来直接执行" ./1.sh ",会输出
"www.baidu.com"
"www.google.com"
如果将1.sh修改为
#!/bin/bash
url="www.baidu.com"
echo ${url}
readonly url
url="www.google.com"
echo ${url}
就会输出两次"www.baidu.com"并报错,因为url是只读变量了所以不能被修改。
删除变量:unset关键字
#!/bin/bash
url="www.baidu.com"
echo ${url}
unset url
echo ${url}
此时执行" ./1.sh “,第一次输出"www.baidu.com”,第二次为空。
变量的种类:
本地变量:生效范围仅作为当前shell进程(其他shell或者子shell无法使用)
例如将a=1,echo$a就是1;然后bash进入子shell,再echo $a就是空值。
[root@node1 ~]# a=1
[root@node1 ~]# echo ${a}
1
[root@node1 ~]# bash # 进入到子shell中
[root@node1 ~]# echo ${a}
环境变量:生效范围为当前shell进程及子进程
变量声明方式1: export varname=“value”
变量声明方式2: declare -x varname=“value”
bash当中有很多内建的环境变量: PATH SHELL
局部变量:生效范围为当前shell进程中某代码片段(通常函数)
位置变量:1,2,3…来表示,让脚本在脚本片段中调用通过命令行传递给他的参数。
用法: ./xx.sh 参数1 参数2 参数3 …
调用:
$1,$2,$3: 对应的是参数1,参数2,参数3....
$0: 对应是命令本身
$*: 传递给脚本的所有参数(把所有参数当成一个整体)
$@: 传递给脚本的所有参数
$#: 传递给脚本的参数个数
案例1:
[root@node1 ~]# cat 1.sh
#!/bin/bash
echo ${1}
echo ${2}
echo $0
echo $*
echo $@
echo $# ·
[root@node1 ~]# ./1.sh Linux Python
Linux
Python
./1.sh
Linux Python
Linux Python
2
案例2:判断所给文件的行数
判断anaconda-ks.cfg文件的行数
[root@node1 ~]# wc -l anaconda-ks.cfg | awk '{print $1}'
48
[root@node1 ~]# cat linecount.sh
#!/bin/bash
lines=`wc -l $1 | awk '{print $1}'`
echo "This file have ${lines} line"
特殊变量:? 0 * @ $ # 等等
shell数组
语法格式:
定义:array_name=(value1 value2 …)
使用:array_name[0]
示例1:
array_1=(A B C)
array_1[0]=A
array_1[1]=B
array_1[2]=C
读取数组: ${array_name[index]}
修改数组中的元素:
array_1[index]=N_value
读取数组中的所有元素:
${array_name[*]}
${array_name[@]}
读取数组中的所有元素个数:
${#array_name[*]}
${#array_name[@]}
实例:
#!/bin/bash
array_1=(A B C D)
echo ${array_1[0]}
echo ${array_1[3]}
echo ${array_1[*]}
echo ${array_1[@]}
echo ${#array_1[*]}
echo ${#array_1[@]}
shell的环境配置
bash配置文件
- 生效范围分类:
全局配置:/etc/profile /etc/profile.d/* /etc/bashrc
个人配置: ~/.bashrc ~/.bash_profile
功能:
- 用于定义环境变量
- 运行命令或脚本
- 功能分类:
- profile类:为交互式的shell提供配置
全局配置:/etc/profile /etc/profile.d/*
个人配置:~/.bash_profile - bashrc类:为非交互式的shell提供配置
全局配置:/etc/bashrc
个人配置:~/.bashrc
功能: 定义本地变量
-
shell登录:
交互式: su - Username
/etc/profile -> /etc/profile.d/* -> ~/.bash_profile
非交互式: su Username
~/.bashrc -> /etc/bashrc编辑环境配置文件: 定义的新设置立即生效: 1. 重新启动shell 2. source命令 -> source /etc/profile
bash算数运算符
算数运算符: + - * / …
增强型: += -= *= /= %=
使用算数符:
(1)let varname=算数表达式
(2)varname=$[算数表达式]
(3)varname=$((算数表达式))
(4)varname=$( expr arg1 arg2 arg3 …)
Note:
乘法符号* 有些情况下需要转义 *
bash中有个内建随机生成器: $RANDOM
练习题
- 计算/etc/passwd文件中第10行的用户ID和第20行的用户ID和
#!/bin/bash
id1=`cat /etc/passwd | head -n 10 | tail -1 | awk -F: '{print $3}'`
id2=`sed -n '20p' /etc/passwd | cut -d: -f3`
let sum_id="id1+id2"
echo $sum_id
head -n 10是前十行,再加上tail -1才是第十行。然后用awk命令过滤,awk -F指定‘:’作为分隔符打印第三个字段,即11。
sed -n '20p’打印文件的第二十行,cut -d指定以‘:’为分隔符,-f3为提取第个三域。
- 传递两个文件文件参数给脚本,计算这两个文件之中所有空白之和
#!/bin/bash
blank1=`cat $1 | grep "^$" | wc -l`
blank2=`cat $2 | grep "^$" | wc -l`
let sum_blank=blank1+blank2
echo $sum_blank
其中“$1”“$2”是传入的文件名参数,执行脚本文件是就可以写成:
./sum_blank.sh 文件名1 文件名2
grep引号中的第一个字符^和最后一个$:
^: 表示字符串开始。
$: 表示字符串结束。
grep "^$"表示精确匹配文件中的空格
wc指令我们可以计算文件的Byte数、字数、或是列数,wc -l只显示行数。
- 统计/etc/,/var/,/usr/目录下所有一级目录和文件之和
#!/bin/bash
sum_etc=`ls /etc/ | wc -l`
sum_var=`ls /var/ | wc -l`
sum_usr=`ls /usr/ | wc -l`
let sum_all="sum_etc+sum_usr+sum_usr"
echo $sum_all
条件测试
bash条件测试:
判断某些需求是否满足,需要由测试机制来实现
专用的测试表达式需要由命令辅助完成测试过程
测试命令: test Experssion
- 测试类型:
数值测试
字符串测试
文件测试
数值测试:
-gt 是否大于
-ge 是否大于等于
-eq 是否等于
-ne 是否不等
-lt 是否小于
-le 是否小于等于
字符串测试:
== : 是否等于
> : 是否大于
< : 是否小于
!=: 是否不等于
=~ : 左侧字符串是否能被右侧的Pattern匹配
Note: 此表达式一般用于 [[]]中
-z “String” : 测试字符串是否为空,空则为真;非空则为假
-n “String” : 测试字符串是否不空,不空为真;空则为假
文件测试:
简单存在性测试:
-a file: 文件存在则为正,不存在则为假
存在及类型测试:
-b file: 是否存在且为块设备文件
-c file: 是否存在且为字符设备文件
-d file: 是否存在且为目录文件
-f file: 是否存在且为普通文件
-h file: 是否存在且为符号链接文件(-l 可以)
-p file: 是否存在且为管道文件
-s file: 是否存在且为socket文件
文件权限测试:
-r file: 是否存在可读权限
-w file:是否存在可写权限
-x file:是否存在可执行权限
文件特殊权限测试:
-g file: 是否存在且拥有sgid权限
-u file: 是否存在且拥有suid权限
-k file: 是否存在且拥有sticky权限
文件大小测试:
-s file: 是否存在且非非空
文件是否打开测试:
- fd : fd表示文件爱你描述是否已经打开且与终端相关
-N file: 文件自动上一次读取之后是否被修改过
-O file:当前用户是否为文件属主
-G file:当前用户是否为文件数组
双目测试:
file1 -ef file2: file1与file2是否指向同一个设上的相关inode
file1 -nt file2: file1是否新于file2
file1 -ot file2: file1是否旧于file2
组合测试条件:
- 第一种方式: && ||
&& 全真则为真
|| 有真则为真 - 第二种方式:
-a -> && -> experssion1 && experssion2
-o -> || -> experssion1 || experssion2
! Experssion
bash退出码
脚本中一旦遇到exit命令,脚本会立即终止,终止退出码取决于exit命令后面的数字
如果脚本中未给出退出码,整个脚本的退出码由最后脚本中最后一行命令的执行结果决定
exit N
练习1: 接收一个文本路径作为参数,如果参数个数小于1,则提示用户“至少给1个参数”,并立即退出
[ $# -lt 1 ] && echo "at least one args ...." && exit 1
$#判断传给脚本的参数个数,小于1就打印“at least one args”并退出,退出码为1。
[ $# -gt 1 ] && echo "test ok ...." && exit 0
于是我们可以根据退出码知道执行情况。
Note: 使用条件测试的时候;[ experssion ]; 条件测试表达式experssion与中括号两边是有空格的
选择执行:
语法:
-
第一种:
if 判断条件;then
条件为真的执行代码块
fi -
第二种:
if 判断条件;then
条件为真的执行代码块
else
条件为假的执行代码块
fi -
第三种:
if 判断条件1;then
条件为真的代码块
elif 判断条件2;then
条件1为假;条件2为真的执行代码块
else
条件12都为假的执行代码块
fi
练习1: 判断两个数是否相等
#!/bin/sh
set num1 = $1
set num2 = $2
#这个判断如何写?
if test $num1 -eq $num2
then
echo "num1 is equal to num2"
else
echo "num1 is not equal to num2"
fi;
练习2: 判断用户是否存在,如果不存在添加用户并设置密码和用户名相同;如果存在立即退出;退出状态码为0
#!/bin/bash
#定义函数
Find_u(){
#判断输入值是否为空,如果为空,则函数结束,返回值1
[ -z $1 ] && return 1
#判断用户是否存在,存在则显示要求,不存在,函数结束,返回值1
if id $1 &> /dev/null ;then
echo "$1 UID is `id -u $1`"
echo "$1 Shell is `grep "^$1:" /etc/passwd \
|cut -d':' -f7 `"
else
return 1
fi
}
#循环执行,以符合题目要求
while :;do
read -p "Please input A username[quit to exit]: " User
if [ $User = quit ];then
exit 0
else
Find_u $User
Res=$?
[ $Res -eq 1 ] && echo "No such $User."
fi
done
知识点: 命令的使用 -> 条件测试 + 选择执行结构 + 状态码
用户交互
read命令
-a : 将内容读取写入到数组中
[root@localhost ~]# read -a array_test1
hello linux test
[root@localhost ~]# echo "get ${#array_test1[*]} values in array"
get 3 values in array
-d : 表示定界符;
-e : 只用于互相交互的脚本
-n : 用于限定最多可以有多少个字符有效读入
-p : 用于给出提示符
echo -n "please input value into array_test "
read -a array_test
或直接
read -p “please input value into array_test…” array_test
示例:
[root@node1 ~]# cat read.sh
#!/bin/bash
read -p "please input one number ......" number1
echo $number1
其中number1是用于接收用户输入的。
-r : 特殊字符生效
-s :对于一些特殊字符不打印的情况
-t :表示等待输入的时间时长
练习1: 输入姓名之后,进行输出
#!/bin/bash
read -p "Input your name" name
echo ${name}
练习2:输入一个文件判断文件类型,判断输入文件是否为目录文件;是则输出yes;否则直接退出
#!/bin/bash
read -p "please input your test file ...." file
if [ -d file ];then
echo "this is a directory file ...."
exit 0
else
echo "this is not a directory file ..."
exit 1
fi
运用条件测试中的具体命令可解决类似题目,可到上文条件测试去看具体命令。
循环结构
循环体: 需要执行的语句,可能执行n遍
for循环
语法:
for 变量名 in 列表;do
循环体
done
执行机制:依次将列表中的元素赋值给变量名然后去执行一遍循环体;当列表中的元素耗尽时,退出
示例:
[root@node1 ~]# for i in `ls /tmp/`;do echo $i;done
循环输出/tmp/下的文件名
[root@node1 ~]# for i in {20,30};do ping -c4 192.168.10.$i;done
分别循环ping 192.168.10.20 和 192.168.10.30 ,输出4行
[root@node1 ~]# for i in `seq 1 10`;do echo $i;done
seq 数字生成器:m到n数字
练习题1: 创建用户user1-user10家目录,并且在user1-user10家目录下创建1.txt - 10.txt文件内容,输出格式为
练习题2: 列出/var/目录下各个子目录所占磁盘大小
while循环
语法:
while 条件测试;do
循环体
done
执行机制: 当条件测试为真是就执行一遍循环体;为假时退出循环
经典使用:读取文件中的内容
写法1:
#!/bin/bash
while read linecontext;do
echo "+++$linecontext"
done < /root/test.txt
写法2:
cat $1 | while read linecontext
do
echo $linecount
done
until循环
执行机制:条件为假时执行循环体;条件为真时退出
语法:
until 条件测试;do
循环体
done
函数
函数定义:
function Fun_name(){
函数体
返回值
}
调用函数:Fun_name
示例1:
#!/bin/bash
function sum_b(){
blank1=`cat $1 | grep "^$" | wc -l`
blank2=`cat $2 | grep "^$" | wc -l`
let sum_blank=blank1+blank2
echo $sum_blank
return 0
}
#调用函数 传入参数
sum_b $1 $2
返回值的获取: 可以通过 echo $?来获取
总结
shell脚本编程要对命令熟悉,通过对命令的组合进行shell编程。
shell脚本应用:通过shell成当前资源(cpu disk vm …)的使用情况 每天都需要统计。