编译型语言:node
程序在执行以前须要一个专门的编译过程,把程序编译成 为机器语言文件,运行时不须要从新翻译,直接使用编译的结果就好了。程序执行效率高,依赖编译器,跨平台性差些。如C、C++正则表达式
解释型语言:shell
程序不须要编译,程序在运行时由解释器翻译成机器语言,每执 行一次都要翻译一次。所以效率比较低。好比Python/JavaScript/ Perl /ruby/Shell等都是解释型语言。vim
总结:数组
编译型语言比解释型语言速度较快,可是不如解释性语言跨平台性好。若是作底层开发或者大型应用程序或者操做系开发通常都用编译型语言;若是是一些服务器脚本及一些辅助的接口,对速度要求不高、对各个平台的兼容性有要求的话则通常都用解释型语言。ruby
回顾一下,Linux操做系统由什么组成的?bash
内核、shell、应用程序、文件系统服务器
shell:命令解释器 人机交互的一个桥梁网络
终端——》命令socket
|
bash shell 解释器(shell)
|
kernel
|
硬件
什么是shell脚本?
简单来讲就是将须要执行的命令保存到文本中,按照顺序执行它。它是解释型的,意味着它不须要编译。
若干命令 + 脚本的基本格式 + 脚本特定语法 + 思想= shell脚本
何时用到脚本?
重复化、复杂化的工做,经过把工做的命令写成脚本,之后仅仅须要执行脚本就能完成这些工做。
如何学习脚本?
一、尽量记忆更多的命令
二、掌握脚本的标准的格式(指定魔法字节、使用标准的执行方式运行脚本)
三、必须熟悉掌握脚本的基本语法(如下列表仅仅的基本要求,还有不少更深更难的语法须要本身扩充学习)
变量定义
条件判断
分支语句
函数
数组
循环语句
正则表达式
sed,awk命令的使用
学习脚本的秘诀:
多看,多写,多思考
看懂脚本——>模仿——>本身写
脚本的编写方法:
非标准:
source xxx.sh
. xxx.sh
bash xxx.sh
sh xx.sh
标准:
格式相对完整的脚本,建议的格式。
#!/bin/bash 脚本第一行 , #!魔法字符,指定脚本代码执行的程序。即它告诉系统这个脚本须要什么解释器来执行,也就是使用哪种Shell
。。。。
脚本执行方法:
标准脚本执行方法(建议):(魔法字节指定的程序会生效)
非标准的执行方法(不建议):(魔法字节指定的程序不会运做)
总结:
./xxx.sh --要求有执行权限,而且必定要声明shell类型(#!/bin/bash)
. xxx.sh或者source xxx.sh或者bash xxx.sh或者sh xxx.sh --不须要有执行权限,也能够不声明shell类型
说明: bash -x xxx.sh 或者sh -x xxx.sh --能够显示执行过程,帮助排错
补充:
bash中的引号:
双引号 "" 会把引号的内容当成总体来看待,容许经过$符号引用其余变量值
单引号 '' 会把引号的内容当成总体来看待,禁止引用其余变量值,shell中特殊符号都被视为普通字符
反撇号 `` 和$() 反撇号和括号里的命令会优先执行,若是存在嵌套,反撇号不能用。
; 可对一行命令进行分割,在执行过程当中不考虑上一个命令执行是不是正确的
&& 逻辑与。可对一行命令进行分割,在执行过程当中考虑上一个命令执行是不是正确的
|| 逻辑或
变量的分类:
本地变量:当前用户自定义的变量。当前进程中有效,其余进程及当前进程的子进程无效。
unset a 取消变量
环境变量:当前进程有效,而且可以被子进程调用。
HI=hello 设置一个本地变量
查看当前用户的环境变量 env
env |grep HI
查询当前用户的全部变量<临时变量与环境变量>
set |grep HI
HI=hello
export HI 将当前变量变成环境变量
env |grep -i HI
HISTSIZE=1000
HI=hello
全局变量:全局全部的用户和程序都能调用,且继承,新建的用户也默认能调用。
/etc/profile
HI=Hello
export HI
/etc/profile
/etc/bashrc
~/.bash_profile
~/.bash_bashrc
user——>login——>/etc/profile——>~/.bash_profile——>/etc/bashrc——>~/.bashrc——>~/.bash_logout
局部变量:
~/.bash_profile
...
注意:须要从新登陆才生效
$HOME/.bashrc 当前用户固定变量 eg:别名
$HOME/.bash_profile 当前用户的环境变量
/etc/bashrc 使用bash shell用户全局变量
/etc/profile 使用全部shell的全局变量
系统变量(内置bash中变量) : shell自己已经固定好了它的名字和做用。$@,$*,$# ,$$ ,$? ,$0
$#:脚本后面接的参数的个数
$*:脚本后面全部参数
$@: 脚本后面全部参数
$?:上一条命令执行后返回的状态,当返回状态值为0时表示执行正常,非0值表示执行异常或出错
若退出状态值为0 表示命令运行成功
若退出状态值为127 command not found
若退出状态值为126 找到了该命令但没法执行 ---权限不够
若退出状态值为1&2 没有那个文件或目录
$$ 当前所在进程的进程号
$! 后台运行的最后一个进程号 (当前终端)
!$ 调用最后一条命令历史中的参数
!! 调用最后一条命令历史
$0 当前执行的进程/程序名
$1~$9 位置参数变量
${10}~${n} 扩展位置参数变量 第10个位置变量必须用{}大括号括起来
vim 3.sh
#!/bin/bash
#xxxxx
echo "\$0 = $0"
echo "\$# = $#"
echo "\$ = $"
echo "\$@ = $@"
echo "\$1 = $1"
echo "\$2 = $2"
echo "\$3 = $3"
echo "\$10 = ${10}"
何时用到变量?
若是某个内容须要屡次使用,而且在代码中重复出现,那么能够用变量表明该内容。这样在修改内容的时候,仅仅须要修改变量的值
在代码运做的过程当中,可能会把某些命令的执行结果保存起来,后续代码须要使用这些结果,就能够直接使用这个变量
变量定义的规则:
一、默认状况下,shell里定义的变量是不分类型的,能够给变量赋与任何类型的值;等号两边不能有空格,对于有空格的字符串作为赋值时,要用引号引发来
955 A=hello
956 echo $A
957 A= hello
958 A =hello
959 A = hello
960 A=hello world
961 A='hello world'
962 echo $A
963 A="hello world"
964 echo $A
二、变量的获取方式: $变量名 ${变量名}
966 echo $A
967 echo ${A}
968 a=123456
969 echo $a
970 echo ${a}
971 echo ${a:2:3}
972 a=123456789
973 echo ${a:3:5} 3表明从第3位开始截取;5表明截取5个数
975 echo ${a:3} 表明截取从第3位开始之后全部的
三、取消变量的命令 unset 变量名
四、区分大小写,同名称但大小写不一样的变量名是不一样的变量
五、变量名能够是字母或数字或下划线,可是不能以数字开头或者特殊字符
[root@node1 shell01]# a=1abc
[root@node1 shell01]# 1a=hello
bash: 1a=hello: command not found
[root@node1 shell01]# ?a=hello
bash: ?a=hello: command not found
[root@node1 shell01]# /a=hello
bash: /a=hello: No such file or directory
[root@node1 shell01]# _a=777
[root@node1 shell01]# echo $_a
777
六、命令的执行结果能够保存到变量
注意:
$( ) 等同于 执行符号 `,可是若是要嵌套使用,使用
符号就不行,要用$();但若是不是嵌套的使用
是能够的,如a="
which mount`which yum
"
七、数组
数组定义:用括号来表示数组,数组元素用“空格”符号分割开。定义数组的通常形式为:
array=(var1 var2 var3 var4)
或者
array[0]=v1
array[1]=v2
array[3]=v3
读取数组:
${array [i]} i表示元素
使用@ 或 * 能够获取数组中的全部元素:
hello,stu1
hello,stu2
hello,stu3
#!/bin/bash
array=(stu1 stu2 stu3)
for var in ${array[*]}
do
echo hello,$var
done
[root@node1 shell01]# var[0]=user1
[root@node1 shell01]# var[1]=user2
[root@node1 shell01]# var[2]=user3
[root@node1 shell01]# for i in ${var[@]};do echo hello,$i;done
hello,user1
hello,user2
hello,user3
获取第n个元素
echo "${user[N-1]}"
获取数组指定元素
echo ${user[@]:1:3} 从数组下标为1开始,读取3个元素
示例:
定义一组用户u01~u05,分别在屏幕上显示hello,username
八、有类型变量
declare
-i 将变量当作整数
-r 使变量只读 readonly
-x 标记变量经过环境导出 export
-a 将变量当作数组
[root@node1 shell01]# a=10
[root@node1 shell01]# b=2
[root@node1 shell01]# c=$a+$b
[root@node1 shell01]# echo $c
10+2
[root@node1 shell01]# declare -i a=5
[root@node1 shell01]# declare -i b=2
[root@node1 shell01]# declare -i c=$a+$b
[root@node1 shell01]# echo $c
7
[root@node1 shell01]#
[root@node1 shell01]# declare -i a=10 b=2
[root@node1 shell01]# declare -i c=$a*$b
[root@node1 shell01]# echo $c
20
1040 echo $a
1041 declare -r a=hello
1042 echo $a
1043 declare -r A=hello
1044 A=888
1045 echo $A
1046 declare -i A=123
1047 AB=hello
1048 export AB
1049 env|grep AB
1050 declare -x ABC=hahaha
1051 env|grep ABC
九、交互式定义变量的值 read 主要用于让用户去定义变量值
1054 read -p "Input your name:" name
1055 echo $name
1056 read -s -p "Input your password:" pass
1057 echo $pass
1058 read -n 5 -p "Input your name:" name
1059 echo $name
1060 read -t 3 -p "Input your name:" name
1061 echo $name
1062 read -t 3 -p "Input your name:" name
1063 echo $name
十、其余变量:
一个“#”表明从左往右去掉一个指定字符
两个“#”表明从左往右最大去掉指定字符
一个“%”表明从右往左去掉一个指定字符
两个“%”表明从右往左最大去掉指定字符
取出一个目录下的目录和文件
/root/Desktop/shell/mem.txt
/root/Desktop/shell
mem.txt
/root/Desktop/shell
/root/Desktop/shell/mem
/root/Desktop/shell/mem
mem.txt
Desktop/shell/mem.txt
1071 aaa=/shell/shell01/dir1/file.txt
1072 echo $aaa
1073 dirname $aaa
1074 basename $aaa
1075 echo ${aaa#/*/}
1076 echo ${aaa##/*/}
1077 echo ${aaa%.*}
1078 echo ${aaa%%.*}
1079 echo ${aaa%/*/}
1080 echo ${aaa%/*}
1081 echo ${aaa%%/*}
===变量内容的替换===
[root@vm1 Desktop]# a=www.taobao.com
[root@vm1 Desktop]# echo ${a/taobao/baidu}
www.baidu.com
[root@vm1 Desktop]# a=www.taobao.com
[root@vm1 Desktop]# echo ${a/a/A}
www.tAobao.com
[root@vm1 Desktop]# echo ${a//a/A} 贪婪替换
www.tAobAo.com
===变量的替代===
aaaaa
111
${变量名:-新的变量值}
变量没有被赋值(包括空值):都会使用“新的变量值“ 替代
变量有被赋值: 不会被替代
简单的四则运算
算术运算:
默认状况下,shell就只能支持简单的整数运算
$(()) | $[] | expr | let
Bash shell 的算术运算有四种方式:
一、使用 $(( ))
二、使用$[ ]
三、使用 expr 外部程式
四、使用let 命令
加法:
n=10
let n=n+1
或者let n+=1
echo $n
乘法:
let m=n*10
echo $m
除法:
let r=m/10
echo $r
求余数:
let r=m%7
echo $r
乘冪:
let r=m**2
echo $r
注意:
n=1
let n+=1 等价于let n=n+1
思考:能不能用shell作小数运算?
2.5
i++ 和 ++i (了解)
对变量的值的影响:
[root@vm1 Desktop]# i=1
[root@vm1 Desktop]# let i++
[root@vm1 Desktop]# echo $i
2
[root@vm1 Desktop]# j=1
[root@vm1 Desktop]# let ++j
[root@vm1 Desktop]# echo $j
2
对表达式的值的影响:
[root@vm1 Desktop]# unset i j
[root@vm1 Desktop]# i=1
[root@vm1 Desktop]# j=1
[root@vm1 Desktop]# let x=i++ 先赋值,再运算
[root@vm1 Desktop]# echo $i
2
[root@vm1 Desktop]# echo $x
1
[root@vm1 Desktop]# let y=++j 先运算,再赋值
[root@vm1 Desktop]# echo $j
2
[root@vm1 Desktop]# echo $y
2
条件判断
语法结构:
if [ condition ];then
command
command
fi
if [ condition ];then
command1
else
command2
fi
if [ condition1 ];then
command1 结束
elif [ condition2 ];then
command2 结束
else
command3
fi
若是条件1知足,执行命令1后结束;若是条件1不知足,再看条件2,若是条件2知足执行命令2;若是条件1和条件2都不知足执行命令3.
if [ condition1 ];then
command1
if [ condition2 ];then
command2
fi
else
if [ condition3 ];then
command3
elif [ condition4 ];then
command4
else
command5
fi
fi
若是条件1知足,执行命令1;若是条件2也知足执行命令2,若是不知足就只执行命令1结束;
若是条件1不知足,不看条件2;直接看条件3,若是条件3知足执行命令3;若是不知足则看条件4,若是条件4知足执行命令4;不然执行命令5
判断语法:
一、test 条件表达式
二、[ 条件表达式 ]
三、[[ 条件表达式 ]] 匹配正则 =~
#!/bin/bash
aaa=$1
[[ $aaa = 'hello' ]] && echo world
[root@node1 shell01]# bash -x if3.sh
aaa=
if3.sh: line 3: [: =: unary operator expected
[root@node1 shell01]# bash -x if3.sh hello
aaa=hello
'[' hello = hello ']'
world
[root@node1 shell01]# vim if3.sh
[root@node1 shell01]# bash -x if3.sh
aaa=
man test去查看,不少的参数都用来进行条件判断
与文件存在与否的判断
-e 是否存在 不论是文件仍是目录,只要存在,条件就成立
-f 是否为普通文件
-d 是否为目录
-S socket
-p pipe
-c character
-b block
-L 软link
文件权限相关的判断
-r 当前用户对其是否可读
-w 当前用户对其是否可写
-x 当前用户对其是否可执行
-u 是否有suid
-g 是否sgid
-k 是否有t位
-s 是否为空白文件 说明:-s表示非空,! -s 表示空文件
[ -s file1 ] file1文件内容不为空,条件成立
[ ! -s file1 ] file1文件内容为空,条件成立
两个文件的比较判断
file1 -nt file2 比较file1是否比file2新
file1 -ot file2 比较file1是否比file2旧
file1 -ef file2 比较是否为同一个文件,或者用于判断硬链接,是否指向同一个inode
整数之间的判断
-eq 相等
-ne 不等
-gt 大于
-lt 小于
-ge 大于等于
-le 小于等于
字符串之间的判断
-z 是否为空字符串 字符串长度为0,就成立
-n 是否为非空字符串 只要字符串非空,就是成立
string1 = string2 是否相等
string1 != string2 不等
! 结果取反
多重条件判断
逻辑判断符号:
-a 和 && 逻辑与 [ 条件1 -a 条件2 ] 只有两个条件都成立,整个大条件才成立
[ 1 -eq 1 ] && [ 2 -ne 3 ]
[ 1 -eq 1 -a 2 -ne 3 ]
-o 和 || 逻辑或 [ 条件1 -o 条件2 ] 只要其中一个条件成立,整个大条件就成立
[ 1 -eq 1 -o 2 -ne 2 ]
[ 1 -eq 1 ] || [ 2 -ne 2 ]
! 逻辑非 优先级最低
-a 优先级 比 -o 优先级要高
#!/bin/bash
aaa=id -u
[ $aaa -eq 0 ] && echo "当前是超级用户" || echo "you不是超级用户"
$ [ $UID -eq 0 ] && echo "当前是超级用户" || echo "you不是超级用户"
demo1:
判断一个IP是否通ping通
方法1:
#!/bin/bash
read -p "请输入你要ping额IP地址:" ip
ping -c1 $ip &>/dev/null 或者 >/dev/null 2>&1
if [ $? -eq 0 ];then
echo "当前主机与所输入的IP网络ok"
else
echo "当前主机与所输入的IP网络不ok"
fi
方法2:
ping -c1 $1 &>/dev/null
test $? -ne 0 && echo "当前主机与所输入的IP网络不ok" || echo "当前主机与所输入的IP网络ok"
demo2:
判断一个进程是否存在
一、ps -ef|grep vsftpd |grep -v grep
二、pidof 程序名称
三、pgrep -l 程序名称
#!/bin/bash
ps -ef|grep vsftpd|grep -v grep &>/dev/null
if [ $? -eq 0 ];then
echo "该进程存在"
else
echo "该进程不存在"
fi
read -p "请输入你须要判断的进程名字:" name
pidof $name &>/dev/null
[ $? -eq 0 ] && echo "该进程存在" || echo "该进程不存在"
pidof $1 &>/dev/null
test $? -ne 0 && echo "该进程不存在" || echo "该进程存在"
需求:使用该脚本判断所输入的进程是否存在(多个进程名,至少2个)
#!/bin/bash
[ $# -eq 0 ] && echo "该脚本$0的用法是:$0 pidname" || pidname=($*)
for i in ${pidname[@]}
do
pidof $i &>/dev/null
test $? -ne 0 && echo "该进程不存在" || echo "该进程存在"
done
pgrep命令:以名称为依据从运行进程队列中查找进程,并显示查找到的进程id
选项
-o:仅显示找到的最小(起始)进程号;
-n:仅显示找到的最大(结束)进程号;
-l:显示进程名称;
-P:指定父进程号;pgrep -p 4764 查看父进程下的子进程id
-g:指定进程组;
-t:指定开启进程的终端;
-u:指定进程的有效用户ID。
demo3:
判断一个服务是否正常(以httpd为例):
一、能够判断进程是否存在,用/etc/init.d/httpd status判断状态等方法
二、最好的方法是直接去访问一下,经过访问成功和失败的返回值来判断
#!/bin/bash
wget -P /tmp http://localhost &>/dev/null
[ $? -eq 0 ] && echo "该服务正常" || echo "该服务不正常"
课堂练习:
一、写一个脚本判断一个用户是否存在
二、完善上一个脚本的bug,要求当没有给脚本传参数或者参数个数不等于1个时,提示脚本的用法:usage:xxx.sh ip
#!/bin/bash
[ $# -ne 1 ] && echo "usage:basename $0
username" && exit
id $1 &>/dev/null
test $? -eq 0 && echo "该用户$1存在" || echo "该用户$1不存在"
#!/bin/bash
[ $# -ne 1 ] && echo "usage:basename $0
ipaddr" && exit
wget http://$1 &>/dev/null
[ $? -eq 0 ] && echo 'httpd服务正常' || echo "httpd服务不正常"
三、判断vsftpd软件包是否安装,若是没有则自动安装
四、判断vsftpd服务是否启动,若是启动,输出如下信息:
vsftpd服务器已启动...
vsftpd监听的地址是:
vsftpd监听的端口是:
#!/bin/bash
ftp=vsftpd
rpm -q $ftp &>/dev/null
[ $? -ne 0 ] && yum -y install $ftp &>/dev/null
pgrep -l $ftp &>/dev/null
[ $? -ne 0 ] && service $ftp start &>/dev/null && echo "服务已经启动..."
ip=netstat -nltp|grep $ftp|cut -d: -f1|cut -c21-
port=netstat -nltp|grep $ftp|cut -d: -f2|cut -d' ' -f1
echo "vsftpd监听的地址是:$ip"
echo "vsftpd监听的端口是:$port"
做业:
一、 判断/tmp/run文件是否存在,若是不存在就创建,若是存在就删除目录里全部文件
#!/bin/bash
dir=/tmp/run
[ -d $dir ] && rm -rf $dir/* || mkdir $dir
dir=/tmp/run
[ -f $dir ] && mv $dir $dir.bak
[ -d $dir ] && rm -rf $dir/* || mkdir $dir
二、 输入一个路径,判断路径是否存在,并且输出是文件仍是目录,若是是字符链接,还得输出是有效的链接仍是无效的链接
#!/bin/bash
read -p "请输入绝对路径:" path
if [ -e "$path" -a -L "$path" ];then
echo "该文件是一个有效链接文件"
elif [ ! -e "$path" -a -L "$path" ];then
echo "该文件是一个无效链接文件"
elif [ -d $path ];then
echo "该文件是一个目录"
elif [ -f $path ];then
echo "该文件是一个普通文件"
elif [ -e $path ];then
echo "其余文件"
else
echo "路径不存在"
fi
三、交互模式要求输入一个ip,而后脚本判断这个IP 对应的主机是否 能ping 通,输出结果相似于:
Server 10.1.1.20 is Down! 最后要求把结果邮件到本地管理员root@localhost和mail01@localhost
四、写一个脚本/home/program,要求当给脚本输入参数hello时,脚本返回world,给脚本输入参数world时,脚本返回hello。而脚本没有参数或者参数错误时,屏幕上输出“usage:/home/program hello or world”
#!/bin/bash
if [ "$1" = "hello" ];then
echo world
elif [ "$1" = "world" ];then
echo hello
elif [ $# -ne 1 ];then
echo "usage:$0 hello or world"
else
echo "usage:$0 hello or world"
fi
五、写一个脚本自动搭建nfs服务
#!/bin/bash
#1.安装软件
#2.确认软件是否安装
#3.配置
#(1).新建共享目录,授本地权限
#(2).发布共享目录/etc/exports
#4.启动服务
#5.设置下次开机自动启动
#配置网络,测试网络
ping -c 1 192.168.0.254 &> /dev/null && echo "##############网络OK###############"
#配置network yum
rm -fr /etc/yum.repos.d/*
cat > /etc/yum.repos.d/dvd.repo << EOT
[base]
baseurl=ftp://192.168.0.254/rhel6_dvd
gpgcheck=0
EOT
#1.安装软件
yum -y install nfs* rpcbind &> /dev/null && echo "##############软件安装OK###############"
#2.xx
#3.配置
#(1).新建共享目录,授本地权限
#read -p "请输入你的共享目录" dir
mkdir -p $1
chmod 777 $1 && echo "##############本地受权OK###############"