一、shell简介
1、语言类型
编译型:
(1)只须编译一次就可以把源代码编译成机器语言,后面的执行无须重新编译,直接使用之前的编译结果就可以;因此其执行的效率比较高;
(2)编译性语言代表:C、C++、Pascal/Object Pascal(Delphi);
(3)程序执行效率比较高,但比较依赖编译器,因此跨平台性差一些;
解释型:
(1)源代码不能直接翻译成机器语言,而是先翻译成中间代码,再由解释器对中间代码进行解释运行;
(2)程序不需要编译,程序在运行时才翻译成机器语言,每执行一次都要翻译一次;
(3)解释性语言代表:Python、JavaScript、Shell、Ruby、MATLAB等;
(4)运行效率一般相对比较低,依赖解释器,跨平台性好;
2、shell定义
1、shell是一种命令解释器,用于连接用户与操作系统内核。
2、shell接收用户输入的命令然后调用内核接口(函数),内核按照shell指令去执行操作,并把结果交给shell,最后由shell将结果呈现给用户。
3、在shell中输入的命令有一部是自带的,叫内置命令。一部分是其他应用程序,叫外部命令。
4、shell命令可以无限拓展(安装软件)
5、shell可以让多个外部程序发生连接
6、shell 也是一种程序设计语言,他有变量,关键字,各种控制语句,有自己的语法结构,利用shell程序设计语言可以编写功能很强、代码简短的程序。
3、shell的分类
# cat /etc/shells 查看系统支持那些shell
/bin/sh
/bin/bash
/sbin/nologin
/usr/bin/sh
/usr/bin/bash
/usr/sbin/nologin
/bin/tcsh
/bin/csh
#chsh –l 查看所有的shell
4、linux命令的分类
内嵌命令 外部命令 别名 函数
别名: alias
查询别名: # alias
设置别名: # alias 命令名称='动作'
取消别名: # unalias 别名名称
历史命令
history, 默认只记录1000条
清除历史命令: history -c
如要每行历史命令前面加上时间
# echo 'export HISTTIMEFORMAT="%F %T "' >> ~/.bashrc
# source ~/.bashrc
多条命令执行用 ; 隔开 :date;whoami
二、shell的特性及特点
1、外部命令与内置命令
内置命令在系统启动时就调入内存,是常驻内存的,所以执行效率高。
而外部命令是系统的软件功能,用户需要时才从硬盘中读入内存
使用type查看是否是内外部
[root@192 ~]# type wget
wget is /usr/bin/wget
[root@192 ~]# type cd
cd is a shell builtin
echo $PATH查看外部命令存储路径
外部:
外部命令也称为文件系统命令,是bash shell之外的程序,它并不是shell 的一部分。外部命令一般位于/bin、/usr/bin、/sbin或/usr/sbin中。
可以使用which查看命令路径加以辨别
当执行外部命令时,Linux系统会创建出一个子进程(这种操作被称为衍生)。
可以使用ps -f 查看
内部:
内置命令和shell是一体的,它们作为shell工具的组成部分存在,不需要通过衍生出子进程来执行,也不需要打开程序文件。
所以内置命令的执行速度要更快,效率也更高。
2、GNU/bash shell特点
1)命令和文件自动补齐
2)命令历史记忆功能 上下键、!number(命令的序号)、!
3)快捷键
Ctrl+a 切换到命令行开始(跟home一样,但是home在某些unix环境下无法使用)
Ctrl+e 切换到命令行末尾
Ctrl+u 清除剪切光标之前的内容
Ctrl+k 清除剪切光标之后的内容
ctrl+y 粘贴刚才所删除的字符
ctrl+左右键 快速移动光标
Ctrl+r 在历史命令中查找,输入关键字调出之前的命令
4)前后台作业控制 &、nohup,^C、^Z(停止),bg (将一个在后台暂停的命令,变成继续执行)、fg (将后台中的命令调至前台继续运行)
5)输入输出重定向 0,1,2 > >> 2> 2>> 2>&1 &> cat < /etc/hosts cat <<EOF cat >file1 <<EOF
6)管道 | tee
7)命令排序
&& || ; 具备逻辑判断,连接命令用的
; 无论前面是否执行成功,分号后的命令都会继续执行
&&:前面执行成功,后面的才继续执行
||:前面命令不成功,后面的命令才会继续
8)shell通配符(元字符)表示的不是本意
* 匹配任意多个字符 ls in* rm -rf * rm -rf *.pdf find / -iname "*-eth0" yum -y install epel*
? 匹配任意一个字符 touch love loove live l7ve; ll l?ve
[] 匹配括号中任意一个字符 [abc] [a-z] [0-9] [a-zA-Z0-9]
() 在子shell中执行(cd /boot;ls) (umask 077; touch file1000)
注意:如何查看当前子shell ps | grep $$ $$:当前shell的pid
{} 集合 touch file{1..9}
\ 转义符,让元字符回归本意
3、shell脚本文件
一般以.sh结尾的文件 .py结尾的文件是python的脚本
第一行一般指定解释器
#!/bin/bash #!/usr/bin/env bash
注释 :以#开有的都不生效
执行shell脚本
1) 直接执行,在命令行写脚本的路径 开启子shell执行,将执行的结果返回到父shell中,前提,脚本要有可执行权限
2) 调用解释器执行
3) source 脚本的绝对路径,点执行 . 脚本的绝对路径 直接在当前shell下执行,会影响当前的shell环境
脚本测试
•sh –x script
这将执行该脚本并显示所有变量的值
•sh –n script
不执行脚本只是检查语法模式,将返回所有错误语法
•sh –v script
执行脚本前把脚本内容显示在屏幕上
三、shell变量
1、变量
1)用一个固定的字符串去表示不固定的内容
2)在内存中开辟一个空间来储存某些变化的值,可以对这个空间进行命名,这个名称就是变量名。
3)变量名=变量值
2、 shell变量类型
四类:自定义变量、环境变量、位置变量、预定义变量
1)自定义变量
变量名=变量值 变量名必须以字母或下划线开头,区分大小写 ip1=192.168.2.115
引用变量: $变量名 或 ${变量名}
查看变量: echo $变量名 set(所有变量:包括自定义变量和环境变量)
取消变量: unset 变量名
作用范围: 仅在当前shell中有效
2)环境变量
定义环境变量: 方法一 export back_dir2=/home/backup
方法二 export back_dir1 将自定义变量转换成环境变量
引用环境变量: $变量名 或 ${变量名}
查看环境变量: echo $变量名
取消环境变量: unset 变量名
变量作用范围: 在当前shell和子shell有效
环境变量拥有可继承性:export之后就拥有继承性
永久生效:写到四个登录脚本,
1)/etc/profile
2)/etc/bashrc
3)~/.bash_profile //配置roo用户的环境变量
4)~/.bashrc /home/tom/.bashrc ///配置tom用户的环境变量
所有用户登录都会加载这四个配置文件
/etc下两个为全局环境配置文件,对所有用户生效
家目录下两个为用户自己的环境配置文件,只对用户本人生效
举例:
vim /etc/profile
JAVA_HOME=/usr/local/java
PATH=$JAVA_HOME/bin:$PATH
export JAVA_HOME PATH
/etc/profile
这是系统最主要的shell设置文件,也是用户登陆时系统最先检查的文件,有关重要的环境变量都定义在此,其中包括PATH,USER,LOGNAME,MAIL,HOSTNAME,HISTSIZE,INPUTRC等。而在文件的最后,它会检查并执行/etc/profile.d/*.sh的脚本。
~/.bash_profile
这个文件是每位用户的bash环境设置文件,它存在与于用户的主目录中,当系统执行/etc/profile 后,就会接着读取此文件内的设置值。在此文件中会定义USERNAME,BASH_ENV和PATH等环境变量,但是此处的PATH除了包含系统的$PATH变量外加入用户的“bin”目录路径.
~/.bashrc
接下来系统会检查~.bashrc文件,这个文件和前两个文件(/etc/profile 和~.bash_profile)最大的不同是,每次执行bash时,~.bashrc都会被再次读取,也就是变量会再次地设置,而/etc/profile,~./bash_profile只有在登陆时才读取。就是因为要经常的读取,所以~/.bashrc文件只定义一些终端机设置以及shell提示符号等功能,而不是定义环境变量。
~/.bash_login
如果~/.bash_profile文件不存在,则系统会转而读取~/.bash_login这个文件内容。这是用户的登陆文件,在每次用户登陆系统时,bash都会读此内容,所以通常都会将登陆后必须执行的命令放在这个文件中。
.profile
如果~./bash_profile ~./bash_login两个文件都不存在,则会使用这个文件的设置内容,其实它的功能与~/.bash_profile相同。
.bash_logout
如果想在注销shell前执行一些工作,都可以在此文件中设置。
例如:
#vi ~.bash_logout
clear
仅执行一个clear命令在你注销的时候
~/.bash_history
这个文件会记录用户先前使用的历史命令。
注意:在/etc/profile.d建立独立的环境变量配置文件
常用环境变量:USER UID HOME HOSTNAME PWD PS1 PATH
PATH:这个变量存放的是所有命令所在的路径 修改:PATH=$PATH:+目录
意义:让所有命令在执行的时候不必输入路径
3)位置变量
位置变量用于命令行、函数或脚本中传递参数。
执行脚本时,通过在脚本后面给出具体参数(多个参数空格隔开)对相应位置变量进行赋值。
$0表示命令本身,$1-$9代表1-9个参数,$10以上需要{}括起来,如${10}
4)预定义变量
$0 脚本名
$* 所有的参数,横的显示
$@ 所有的参数,竖的显示
$# 参数的个数
$$ 当前进程的PID
$! 上一个后台进程的PID
$? 上一个命令的返回值 0表示成功 ,1-254 表示不成功
3、变量的赋值
1)直接赋值
a=3
2)从键盘赋值
read -p [提示信息]:[变量名]
3)使用命令行参数赋值(利用位置变量传参)
4)利用命令的输出结果赋值
在需要复制到语句中使用反单引号
5)从文件中读取数据进行赋值
定义或引用变量时注意事项:
" " 弱引用 可以实现变量和命令的替换
' ' 强引用 不完成变量替换
` ` 命令替换 等价于 $() 反引号中的shell命令会被先执行
4、shell变量的运算
shell是弱语言,shell中变量值的类型默认全部都是字符串,不能直接运算,如需运算需要使用特殊方法。
1. 整数运算
方法一:expr
expr 1 + 2
expr $num1 + $num2
运算符 :+ - \* / %
方法二:$(())
echo $(($num1+$num2))
echo $((num1+num2))
echo $((5-3*2))
echo $(((5-3)*2))
echo $((2**3))
sum=$((1+2)); echo $sum
运算符:+ - * / %
方法三:$[]
echo $[5+2]
echo $[5**2]
运算符: + - * / %
方法四:let
let sum=2+3; echo $sum
let i++; echo $i
2. 计算小数
echo 1.5+1.5 | bc
3.0 //会显示跟计算的小数点一样的位数,bc命令没有的话,安装 yum install bc -y
四、shell条件测试
1、文件测试
语法:test 条件表达式
test -f file && echo true || echo false
[ 条件表达式 ] 两边要空格
[ -f file ] && echo true || echo false
[[ 条件表达式 ]] 支持正则
if [[ $1 =~ ^[0-9]+$ ]] then ... else... fi
文件测试操作符:
-d 测试是否为目录
-a 测试目录或文件是否存在
-f 测试是否为文件
-r 测试当前用户是否可读
-w 测试当前用户是否可写
-x 测试当前用户是否可执行
2、整数测试
整数测试常用于数值之间的运算
语法:[ 整数1 操作符 整数2 ]
整数测试操作符:
-eq 等于
-ne 不等于
-gt 大于
-lt 小于
-le 小于或等于
-ge 大于或等于
3、字符串测试
包括比较字符串是否相同,测试字符串长度是否为0.
语法:[ 字符串 1 = 字符串2 ]
字符串测试运算符:
-z 判断字符串长度是否为0
-n 判断字符串长度是否为非0
!= 判断两个字符串是否不相等
=判断两个字符串是否相等
4、if条件语句
1)单分支
if [ 条件表达式 ]
then
代码块
fi
2)双分支
if [ 条件表达式 ]
then
代码块
else
代码块
fi
3)多分支
if [ 条件表达式 ]
then
代码块
elif
代码块
else
代码块
fi
4)多重条件判断
条件表达式1 选项 条件表达式2
选项:
-a:并且
-o :或者
! 条件表达式 :取反,非
if [ $username = "root" -a $password = "redhat" ]
if [ $username = "root" ] && [ $password = "redhat" ]
if [[ $username = "root" && $password = "redhat" ]]
5、case条件语句
case看起来比if语句更加工整,企业在实现系统服务启动脚本时偏向于case。
case 变量值 in
条件表达式1 )
代码块1
;;
条件表达式2)
代码块2
;;
条件表达式3)
代码块3
;;
*)
无匹配后代码块
esac
条件表达式匹配:
* 任意字符
? 任意单个字符
[abc] a、b、c其中之一
[a-n] 从a到n中任意一个
| 多重选择
五、shell循环
1、 for循环
for循环主要用于固定次数的循环,不能用于守护进程及无限循环。
语法结构:
1)
for 变量名 in 取值列表
do
循环体
done
2)
for ((i=1;i<=10;i++))
do
动作块
done
注意:for循环读文件时是以空格为分隔符开始读的,如不想以空格为分割符,可以在脚本中自定义分割符IFS=$''/n(以换行符为分割符)。
2、while循环
语法:
while 条件测试
do
循环体
done
while循环语句会对条件测试进行判断,如果条件成立时,则执行do和done之间的循环体,直到条件测试不成立时才停止循环。
注意:空命令":"与true命令作用相同,在while循环和if/then中也可以使用。如下:
1)while死循环
while :
do
代码块
done
2)if/then中引出分支
if 条件
then : #什么也不做,引出分支
else
action
fi
3、for循环与while循环比较
1)for循环写ping脚本
#!/bin/bash
for i in {2..254}
do
{
ip=192.168.100.$i
ping -c 3 $ip &>/dev/null
if [ $? -eq 0]
then
echo "$ip is up."
}&
done
wait
echo "all finish..."
2)while循环写ping脚本
#!/bin/bash
i=2
while [ $i -le 254 ]
do
{
ip=192.168.100.$i
ping -c 3 $ip&>/dev/null
if[ $? -eq 0 ]
then
echo "$ip is up."
fi
}&
let i++
done
wait
echo "all finish..."
4、expect
expect是一种编程语言,用来实现自动和交互任务进行通信,需要指定#!/usr/bin/expect来进行解释。如在shell脚本中进行使用1.在脚本中进行expect脚本调用
/usr/bin/expect script.exp 2.把expect代码放在/usr/bin/expect <<EOF 代码 EOF 中进行使用。
expect实现批量主机公钥推送:
#!/bin/bash
#检测是否安装expect软件
rpm -ne expect &>/dev/null
if [ $? -eq 0 ];then
yum -y install expect
if [ $? -eq 0 ];then
echo "install success!"
else
echo "install fail!"
exit 2
fi
fi
#检查客户端是否生成公私钥
if [ !-f ~/.ssh/id_rsa ];then
ssh-keygen -p "" -f~/.ssh/id_rsa
if [ $? -eq 0 ];then
echo "success!"
else
echo "fail!"
exit 2
fi
fi
#检查客户端是否可以ping通,ping通推送密钥
>ip.txt
for i in {2..254}
do
{
ip=192.168.100.$i
ping -c1 -w1 $ip &>/dev/null
if [ $? -eq 0 ];then
echo "$ip">>ip.txt
#ping通一个推送一个
/usr/bin/expect<<-EOF
set timeout 10
spawn ssh-copy-id $ip
expect {
"yes/no"{send "yes\r";exp_contiue}
"passwd:"{send "123\r" }
}
expect eof
EOF
fi
}&
done
wait
echo "finish..."
注意:ssh-keygen -p(指定密码为空)-f(指定保存密钥的文件名)
5.seq
seq 1 2 10 //打印奇数 起始位置 步长 结束位置步长
seq 5 10 //打印5到10 起始位置 结束位置 ,步长为1
seq -w 10 //等宽显示 :不够位数在前面补0
-s指定输出的分隔符,默认为\n,即默认为回车换行
六、shell数组
1、数组概念
数组中有限个相同类型的变量用一个名字命名,然后用编号区分它们。用于区分不同元素的编号称为数组下标,数组的元素有时也称为下标变量。
2、shell数组的类型
数组分为普通数组和关联数组。
普通数组:数组元素的索引(下标)从0开始编号,获取数组中的元素要利用索引(下标)。索引可以是算数表达式,其结果必须是一个整数。
普通数组的定义:
books=(Linux shell awk)#定义普通数组,python中称为列表
关联数组:关联数组跟普通数组的不同是,他的索引下标是任意的整数和字符串。
关联数组的定义:
info=([name]=linux [sex]=male)
#在python中称为字典
索引下标为字符串不必为整数,如:name sex
3、定义数组的类型
用户定义的是普通数组,如需使用关联数组需先声明再使用。声明关联数组使用-A参数,通常情况下shell解释器隐式声明普通数组,用户无需操作。
声明普通数组:declare -a array
声明关联数组:declare -A array
4、数组的定义
1)直接定义
数组名=(变量值1 变量值2 变量值3 变量值4...)
2)下标定义数组
数组名=([下标1]=变量值1 [下标2]=变量值2...)
例:
declare -A info1
info1=([name]=Linux [age]=18)
echo ${info1[age]}
3)间接定义数组
数组名[下标]=变量值1;数组名[下标]=变量值2
如:array[0]=linux
4)从文件中读入定义数组
数组名=($(`变量值`))或数组名=(`变量值`)
如:
array=(`cat /etc/passwd`)
echo ${array[*]}
5、访问数组的方式
echo ${!array[*]} 访问数组的所有索引
echo ${!array[@]} 访问数组的所有索引
echo ${array[*]} 访问数组所有值
echo ${array[@]} 访问数组所有值
echo ${#array[@]} 统计数组元素个数
echo ${array[0]} 访问数组中第一个元素
6、数组的遍历
1)while
#!/bin/bash
while read line
do
hosts[++i]=$line
done</etc/hosts
for i in ${!hosts[@]}
do
echo "$i:${hosts[i]}"
done
2)for
#!/bin/bash
IFS=$'\n'
for i in `cat /etc/hosts`
do
hosts[++j]=$line
done
for i in ${!hosts[@]}
do
echo "$i:${hosts[i]}"
done
7、数组的删除
unset数组[下标 ]删除相应数组元素,不带下标,表示删除整个数组的所有元素
unset array[1]
8、使用数组统计TCP连接状态
#!/bin/bash
while true
do
unset status
declare -A status
type=`ss -an|grep :80` |awk '{print $2}'
for j in ${!status[@]}
do
echo "$j:${status[$j]}"
done
sleep 1
clear
done
七、shell函数
1、shll函数的语法
函数名(){
函数要实现功能的代码
}
或:
function 函数名(){
函数要实现功能的代码
}
2、函数的调用
函数只有被调用才能被执行,调用指定的函数名,函数出现的地方会被自动替换为函数代码。
如:在脚本中使用,放在只包含函数的单独文件中使用,交互环境中使用。
最基本语法格式:
函数名
单参数的语法格式:
函数名 参数1 参数2
3、函数的返回值介绍
默认情况下,在执行完函数最后一行代码后,函数最终会返回一个状态数字,这个时候可以使用$?一个变量来查看函数执行状态,如返回0表示方法正常退出,非0表示程序发生错误或非正常退出。使用 return(范围0-255)关键字来返回一个整数,其作用是退出函数。
如想返回字符串或其他类型,可以使用echo变量来返回值。
示例:
#!/bin/bash
num=(1 2 3)
num2=(2 3 4)
array() {
local newarray=($*)
local i
for ((i=0;i<$#;i++))
do
newarray[$i]=$( ( ${newarray[$i]}*5 ) )
done
echo "${newarray[*]}"
}
result=`array ${num[*]}`
echo ${result[*]}
result=`array ${num2[*]}`
echo ${result[*]}
4、循环结构中break,continue,return,exit的区别
break:应用于switch语句和循环语句中,break命令表示直接退出循环,执行循环结构下面的第一条结构。
continue:用于for、while、repeat语句中结束循环内的本次处理,继续从循环体的开始位置继续执行。
return:在函数中将数据返回,或返回一个结果给调用函数的脚本,只退出函数,不退出脚本。
exit:退出当前shell程序,退出脚本。