shell脚本

一、shell概念
Shell 是一个用C语言编写的程序,它是用户使用Linux的桥梁。
Shell 脚本(shell script),是一种为shell编写的脚本程序。

二、Shell运行环境
Linux系统上有运行shell的解释器的环境下都可以运行shell脚本
前市面上较知名的发行版有:Ubuntu、RedHat、CentOS都自带了解释器

三、Linux下shell的解释器类型:
/bin/sh
/bin/bash
/bin/tcsh
/bin/ksh

四、shell运行方式:
(1).第一种方式:运行时指定解释器
vi test01.sh
echo "ni hao !"
运行脚本:
/bin/bash ./test01.sh
/bin/sh ./test01.sh

(2).第二种方式:在脚本的第一行指定解释器
vi test012.sh
#!/bin/sh
#echo "124"
echo "456"
给shell脚本添加可执行权限
chmod u+x ./test02.sh
运行shell脚本:
./test02.sh
结果:
456

五、shell语法操作:
(1).Shell 变量
定义shell变量时最好。。。按java的命名规则定义变量名
注意,变量名和等号之间不能有空格
变量名=值
1.案例01:
vi test03.sh
#!/bin/bash
name="zhang"
sex='nan'
echo $name
echo $sex

注意:shell脚本语言都可以使用单引号被双引号;
A.单引号字符串的限制:
单引号里的任何字符都会原样输出,单引号字符串中的变量是无效的;
单引号字串中不能出现单引号(对单引号使用转义符后也不行)。
#!/bin/bash
name="zhangyanpeng"
sex='leihao'
echo 'my name is $name'
输出结果:
my name is $name

B.双引号的优点:
双引号里可以有变量
双引号里可以出现转义字符
#!/bin/bash
name="zhangyanpeng"
sex='leihao'
echo "my name is $name"
输出结果:
my name is zhangyanpeng


只读变量 readonly

#!/bin/bash
name="zhangyanpeng"
echo "my name is $name"
readonly name
name="wo"
echo "$name"

运行:./test03.sh
输出结果:
./test03.sh: line 4: name: readonly variable
zhangyanpeng
my name is zhangyanpeng


删除变量:unset
#!/bin/bash
price=13.5
echo "$price"
unset price
echo "$price"

获取字符串的长度:
vi ./test04.sh
#!/bin/bash
str="ABCD"
echo "${#str}"


./test04.sh
4


符串第 2 个字符开始截取 4 个字符
vi ./test05.sh
#!/bin/bash
str="ABCDEF"
echo "${str:2:4}"

./test05.sh
CDEF


(2).素组
bash支持一维数组(不支持多维数组),并且没有限定数组的大小。
类似与C语言,数组元素的下标由0开始编号。获取数组中的元素要利用下标,下标可以是整数或算术表达式,其值应大于或等于0。

定义数组
students=("zhangyangpeng" "changjiale" "laopan")
或者:
students=(
"zhangyangpeng"
"changjiale"
"laopan"
)

获取素组的值:
${数组名[下标]}
获取第一个元素
echo "${students[0]}"

./arra01.sh
zhangyangpeng

获取数组的长度:
echo "${#students[@]}"
echo "${#students[*]}"


Shell 注释
以"#"开头的行就是注释,会被解释器忽略。
sh里没有多行注释,只能每一行加一个#号。
注意:第一行除外!

(3).Shell 基本运算符
算数运算符
关系运算符
布尔运算符
字符串运算符
文件测试运算符


bash不支持简单的数学运算,但是可以通过其他命令来实现,例如 awk 和 expr、seq,expr 最常用。
expr 是一款表达式计算工具,使用它能完成表达式的求值操作。
例如,两个数相加(注意使用的是反引号 ` 而不是单引号 '):
A.算数运算符
+
-
*
/
%

vi test06.sh
#!/bin/bash
echo "$0"
echo "$1"
echo "$2"

v1=`expr $1 + $2`
v2=`expr $1 - $2`
v3=`expr $1 * $2`
v4=`expr $1 / $2`
v5=`expr $1 % $2`
echo "$v1"
echo "$v2"
echo "$v3"
echo "$v4"
echo "$v5"

运行脚本:
./test06.sh 100 80
输出结果:
./test06.sh --->$0 表示第一个参数 脚本文件名称自己了
100 --->$1 表示第二个参数 100
80 --->$2 表示第三个参数 80
180
20
8000
1
20

B.关系运算符
关系运算符只支持数字,不支持字符串,除非字符串的值是数字。
假定变量 a 为 10,变量 b 为 20
-eq 检测两个数是否相等,相等返回 true。 [ $a -eq $b ] 返回 false。 --->equal
-ne 检测两个数是否相等,不相等返回 true。 [ $a -ne $b ] 返回 true。not equal
-gt 检测左边的数是否大于右边的,如果是,则返回 true。 [ $a -gt $b ] 返回 false。-->greater than
-lt 检测左边的数是否小于右边的,如果是,则返回 true。 [ $a -lt $b ] 返回 true。 -->less than
-ge 检测左边的数是否大于等于右边的,如果是,则返回 true。 [ $a -ge $b ] 返回 false。-->greater equal
-le 检测左边的数是否小于等于右边的,如果是,则返回 true。 [ $a -le $b ] 返回 true ---> less equal


#!/bin/bash
a=10
b=20

if [ $a -eq $b ];then
echo "$a -eq $b : a 等于 b"
else
echo "$a -eq $b: a 不等于 b"
fi

if [ $a -ne $b ]
then
echo "$a -ne $b: a 不等于 b"
else
echo "$a -ne $b : a 等于 b"
fi

if [ $a -gt $b ]
then
echo "$a -gt $b: a 大于 b"
else
echo "$a -gt $b: a 不大于 b"
fi

if [ $a -lt $b ]
then
echo "$a -lt $b: a 小于 b"
else
echo "$a -lt $b: a 不小于 b"
fi

if [ $a -ge $b ]
then
echo "$a -ge $b: a 大于或等于 b"
else
echo "$a -ge $b: a 小于 b"
fi

if [ $a -le $b ]
then
echo "$a -le $b: a 小于或等于 b"
else
echo "$a -le $b: a 大于 b"
fi


C.布尔运算符、逻辑运算符
! 非运算,表达式为 true 则返回 false,否则返回 true。 [ ! false ] 返回 true。
-o、|| 或运算,有一个表达式为 true 则返回 true。 [ $a -lt 20 -o $b -gt 100 ] 返回 true。
-a、&& 与运算,两个表达式都为 true 才返回 true。 [ $a -lt 20 -a $b -gt 100 ] 返回 false。

#!/bin/sh
#if [ $1 -eq $2 ] || [ $1 -gt $2 ];then
#if [ $1 -eq $2 ] && [ $1 -gt $2 ];then
#if [[ $1 -eq $2 && $1 -gt $2 ]];then
if [[ $1 -eq $2 || $1 -gt $2 ]];then
echo "走if"
else
echo "走else"
fi
if [ $1 -eq $2 -o $1 -gt $2 ];then
#if [[ $1 -eq $2 -a $1 -gt $2 ]];then
echo "走if"
else
echo "走else"
fi

if ! [ $1 -eq $2 ];then
echo "走if"
else
echo "走else"
fi


D.字符串运算符
= 检测两个字符串是否相等,相等返回 true。 [ $a = $b ] 返回 false。
!= 检测两个字符串是否相等,不相等返回 true。 [ $a != $b ] 返回 true。
-z 检测字符串长度是否为0,为0返回 true。 [ -z $a ] 返回 false。
-n 检测字符串长度是否为0,不为0返回 true。 [ -n $a ] 返回 true


a="abc"
b="efg"

if [ $a = $b ]
then
echo "$a = $b : a 等于 b"
else
echo "$a = $b: a 不等于 b"
fi
if [ $a != $b ]
then
echo "$a != $b : a 不等于 b"
else
echo "$a != $b: a 等于 b"
fi
if [ -z $a ]
then
echo "-z $a : 字符串长度为 0"
else
echo "-z $a : 字符串长度不为 0"
fi
if [ -n $a ]
then
echo "-n $a : 字符串长度不为 0"
else
echo "-n $a : 字符串长度为 0"
fi
if [ $a ]
then
echo "$a : 字符串不为空"
else
echo "$a : 字符串为空"
fi

E.文件测试运算符
-d file 检测文件是否是目录,如果是,则返回 true。 [ -d $file ] 返回 false。
-f file 检测文件是否是普通文件(既不是目录,也不是设备文件),如果是,则返回 true。 [ -f $file ] 返回 true
-r file 检测文件是否可读,如果是,则返回 true。 [ -r $file ] 返回 true。
-w file 检测文件是否可写,如果是,则返回 true。 [ -w $file ] 返回 true。
-x file 检测文件是否可执行,如果是,则返回 true。 [ -x $file ] 返回 true。
-s file 检测文件是否为空(文件大小是否大于0),不为空返回 true。 [ -s $file ] 返回 true。
-e file 检测文件(包括目录)是否存在,如果是,则返回 true。 [ -e $file ] 返回 true。

#!/bin/bash
file=/shell/test01.sh
if [ -d $file ];then
echo "$file is directory"
else
echo "$file is file"
fi
echo "---------------------------"
if [ -f $file ];then
echo "$file is file"
else
echo "$file is not file"
fi
echo "---------------------------"

#if [ -w $file ];then
if [ -x $file ];then
#if [ -r $file ];then
echo "$file is excute enable"
else
echo "$file is not excute"
fi
echo "---------------------------"
if [ -e $file ];then
echo "$file is exists"
else
echo "$file is not exists"
fi
echo "---------------------------"
if [ -s $file ];then
echo "$file is 0"
else
echo "$file is not 0"
fi
echo "---------------------------"


F.shell的流程控制
(1).if判断

结构1:
if [ ]
then
comand
fi

结构2:if else
if [ ]
then
comand1
else
comand2
fi

结构3:if elif
if [ ]
then
comand1
elif
then
comand2
fi


结构4:if elif else
if [ ]
then
comand1
elif [ ]
then
comand2
else
comand3
fi


vi test10.sh

#!/bin/sh
a=$1
b=$2
if [ $a -eq $b ]
then
echo "相等!"
fi
echo "--------------------"
if [ $a -eq $b ]
then
echo "相等!"
else
echo "不相等!"
fi

echo "--------------------"
if [ $a -gt $b ]
then
echo "a大于b"
elif [ $a -lt $b ]
then
echo "a小于b"
else
echo "other"
fi

运行:
./test10.sh 5 8

--------------------
不相等!
--------------------
a小于b

(2). for循环
for x in .....
do
command...
done

vi test11.sh
#!/bin/bash
#for循环方式一:
echo "=====for in============="
for a in 1 2 3 4 5 6 7
do
echo "$a"
done

echo "=====for in案例============="
for filename in `ls ./`
do
echo "$filename"
done

#for循环方式二:
echo "=====for((i=0;i<=10;i++))============="
for((i=0;i<=10;i++))
do
echo "$i"
done

#for循环方式三:
echo "=====for va in seq 1 10============="
for va in `seq 1 10`
do
echo "$va"
done


(3).while循环
while(())
do
comand....
done

vi test12.sh
#!/bin/sh
i=0
while(($i<5))
do
if [ $i -eq 3 ];then
i=`expr $i + 10`
fi
#let "i++"
i=`expr $i + 1`
echo "$i"
done

(4).case --- esac
case 值 in
模式1)
command1
command2
...
commandN
;;
模式2)
command1
command2
...
commandN
;;
esac

#!/bin/sh
echo "请输入密码:"
read num
case $num in
1|3|5|7|9)
echo "您输入的值为:$num"
;;
2|4|6|8)
echo "您输入的值为:$num"
;;
*)
echo "您输入的值为:$num"
;;
esac

综合案例:continue/break/while/case/if
vi test13.sh
n=1
while(($n<10))
do
case $n in
1|3|5)
if [ $n -eq 3 ];then
let "n++"
continue;
fi
echo "n=$n"
;;
2|4|6)
if [ $n -eq 6 ];then
break;
fi
echo "n=$n"
;;
esac
let "n++"
# n=`expr $n + 1`
done

G.shell函数:
[ function ] funname [()]{
action;
[return int;]
}

vi test14.sh
#!/bin/bash
echo "方法先定义再调用..."
function f1(){
echo "f1()"
}
f1
echo "----------return--"
function f2(){
#return ` expr 1 + 2`
a=`expr 1 + 2`
}
f2
# $?获取f2方法的返回值
echo "$?"

echo "------参数--"
function f3(){
# $1 和 $2 表示是方法调用时传递的参数
a=`expr $1 + $2`
return $a
}

f3 10 20
echo "$?"
# $1 和 $2 表示是运行脚本时传递的参数
b=`expr $1 + $2`
echo $b


综合案例
1、删除当前目录下大小为0的文件
#!/bin/sh
cd $1
for v in `ls ./`
do
if ! [ -s $v ]
then
rm -rf "$v"
if [ "$?" -eq 0 ];then
echo "rmove the $v is successfully!"
else
echo "rmove the $v is fail"
fi
fi
done

运行
/bin/sh ./demo01.sh /shell/folder

2、给某个文件夹下所有文件问好
3、列出某个路径下所以文件的路径(非文件夹)
4、判断hdfs文件是否存在

#!/bin/sh
hd=$(which hadoop)
$hd fs -test -e /$1
if [ $? -eq 0 ];then
echo "the $1 is exists "
fi

作业1:上传文件到hdfs系统 判断是否存在-->上传--->判断上传是否成功
作业2:删除hdfs系统的文件 判断是否存在-->删除--->判断删除是否成功


5、模拟(删除/远程拷贝)一周前的日志文件
模拟文件:
2017-02-21.log
2017-03-01.log
2017-03-02.log
2017-03-21.log
2017-03-22.log
2017-03-23.log

#!/bin/bash
cd $1
#先获取7天前的秒值时间
times_7days_ago=`date -d '7 days ago' +%s`
for filename in `ls ./`
do
# use the "basename --help" to get the command help
#获取文件名的时间:2017-03-01.log ->2017-03-01
times=`basename $filename .log`
filenametimes=`date -d "$times" +%s`
if [ $times_7days_ago -gt $filenametimes ];then
rm -rf $filename
if [ $? -eq 0 ]
then
echo "the $filename file delete successfully!"
fi
fi
done

执行脚本:
./rmlog.sh /shell/logfolder/

(7题扩展作业,定时器操作、shell脚本)
(1)使用脚本生产日志文件每分钟生产一个log文件(文件格式:2016-12-22-00-01.log、2016-12-22-00-02.log,内容随便)
(2)使用脚本检查删除十分钟前的日志
(3)使用脚本完成将一周前的日志文件拷贝到其它节点。

指定1970年以来的秒数:
date -d ’1970-01-01 1251734400 sec utc’ (2009年 09月 01日 星期二 00:00:00 CST)
date -d ’1970-01-01 1314177812 sec utc’ (2011年 08月 24日 星期三 17:23:32 CST)
今天:
date
date -d today
date -d now
明天:
date -d tomorrow
date -d next-day
date -d next-days
date -d “next day”
date -d “next days”
date -d “+1 day”
date -d “+1 days”
date -d “1 day”
date -d “1 days”
date -d “-1 day ago”
date -d “-1 days ago”
昨天:
date -d yesterday
date -d last-day
date -d last-days
date -d “last day”
date -d “last days”
date -d “-1 day”
date -d “-1 days”
date -d “1 day ago”
date -d “1 days ago”
前天:
date -d “2 day ago”
date -d “2 days ago”
date -d “-2 day”
date -d “-2 days”
大前天:
date -d “3 day ago”
date -d “3 days ago”
date -d “-3 day”
date -d “-3 days”
上周,一周前:
date -d “1 week ago”
date -d “1 weeks ago”
上个星期五(不是上周五):
date -d “last-friday”
date -d “last friday”
上月,一月前:
date -d last-month
date -d last-months
date -d “-1 month”
date -d “-1 months”
下月,一月后:
date -d next-month
date -d next-months
date -d “+1 month”
date -d “+1 months”
去年,一年前:
date -d last-year
date -d last-years
date -d “-1 year”
date -d “-1 years”
明年,一年后:
date -d next-year
date -d next-years
date -d “+1 year”
date -d “+1 years”
一小时前:
date -d “last-hour”
date -d “last-hours”
date -d “1 hour ago”
date -d “1 hours ago”
一小时后:
date -d “1 hour”
date -d “1 hours”
一分钟前:
date -d "1 minute ago"
date -d “1 minutes ago”
一分钟后:
date -d “1 minute”
date -d “1 minutes”
一秒前:
date -d “1 second ago”
date -d “1 seconds ago”
一秒后:
date -d “1 second”
date -d “1 seconds”


awk语法:
1.输出文件的每一行:
awk '{print $0}' ./employee.txt
2.输出/etc/passwd第一个字段
awk -F ":" '{print $1}' /etc/passwd

cat employee.txt
100 Thomas Manager Sales 5000
200 Jason Developer Technology 5500
300 Sanjay Sysadmin Technology 7000
400 Nisha Manager Marketing 9500
500 Randy DBA Technology 6000

----------------------------------------------------------
#awk命令格式

(1) awk [-F 分隔域] 'command' input-file(s)
(2) awk -f awk-script-file input-file(s)
----------------------------------------------------------
#打印文件的全部内容
awk '{print $0}' employee.txt

----------------------------------------------------------
#抽取文件test中的第一列
awk '{print $1}' employee.txt
或者
awk -F ' ' '{print $1}' employee.txt
----------------------------------------------------------
#列出所有的用户名和登陆的shell名
awk -F ':' '{print $1,$6}' /etc/passwd
当分隔符为多个符号时,如:
a , b , c , d
a1 , b1 , c1 , d1
awk -F ' , ' '{print $1,$2}' 文件名
----------------------------------------------------------
#打印用户名为root的那一行
awk -F ':' '$1=="root" {print $0}' /etc/passwd
或者
awk -F ':' '$1=="keke" {print $1}' /etc/passwd

说明:$1=="root"和$1=="keke"都是属于判断条件
----------------------------------------------------------
#给输出信息加上表头
awk -F ":" 'BEGIN {print "name shell --------------------------------"}
{print $1" "$6}' /etc/passwd

----------------------------------------------------------
#给输出信息加上表头和末尾
awk -F : 'BEGIN {print "name shell --------------------------------"}
{print $1" "$6} END {print "end-of-report"}' /etc/passwd

awk -F ":" 'BEGIN {print"--BEGIN--"}
$1=="root" { print $1}
END{print"----END------"}' /etc/passwd

awk -F ":" 'BEGIN {print"--BEGIN--"}
{if( $1=="root") print $1}
END{print"----END------"}' /etc/passwd

----------------------------------------------------------
正则表达式匹配格式
1)$n~正则表达式
2)if($n~正则表示式) print $0
----------------------------------------------------------
#打印以root开头的行
awk -F ":" '$0 ~ /^root/' /etc/passwd
awk -F: '{if($0 ~ /^root/) print $0}'
#打印以ntp开头并以login结尾的行
awk -F : '$0 ~ /^ntp.*login$/ ' /etc/passwd
#打印包含nobody的行
awk -F : '{if($1~/nobody/) print $0}' /etc/passwd
#打印包含nobody或Nobody的行
awk -F : '{if($1~/[Nn]obody/) print $0}' /etc/passwd
----------------------------------------------------------
#精确匹配
#打印名字为root的用户在/etc/passwd文件中的记录
awk -F : '$1=="root" {print $0}' /etc/passwd
awk -F : '{ if($1=="root") print $0 }' /etc/passwd
----------------------------------------------------------
在awk中使用条件操作符
< 小于
>= 大于等于
<= 小于等于
== 等于
!= 不等于
~ 匹配正则表达式
!~ 不匹配正则表达式
----------------------------------------------------------
#或/与运算
awk -F : '$1~/(root|shawn)/ {print $0}' /etc/passwd
awk -F : '$1~/root/ || $1 ~/shawn/ {print $0}' /etc/passwd
awk -F : '{if($1~/root/ || $1 ~/shawn/) print $0}' /etc/passwd
awk -F : '$6~/home/ && $6~/shawn/ {print $0}' /etc/passwd
awk -F : '{if($1~/root/ || ($1~/shawn/)) print $0}' /etc/passwd
awk -F : '{if($1~/root/ || ($1~/shawn/)){print $0} else {print "other:"$0}}' /etc/passwd
awk -F : '{if($0!~/^ntp.*nologin$/) print $0}' /etc/passwd
----------------------------------------------------------
#内置变量
ARGC 命令行参数个数
ARGV 命令行参数排列
FS 设置输入域分隔符,与-F同
NF 记录域的个数
NR 已读的记录数
OFS 输出域分隔符
ORS 输出记录分隔符
RS 控制记录分隔符
----------------------------------------------------------
#FS的用法,注意FS要加双引号
awk 'BEGIN {FS=":"}{print $1}' /etc/passwd
----------------------------------------------------------
#内置变量用法
BEGIN {
FS=":"
}
{
print $1,$6
}
----------------
BEGIN {
FS=":"
OFS="="
}
{
print $1,$6
}
----------------
BEGIN {
FS=":"
OFS="="
}
{
print "Row " NR ":" $1,$6
}
----------------
BEGIN {
FS=":"
OFS="="
}
{
print "Row " NR ":" $1,$6,$NF
}
END{
print "当前共有" NF "列"
}
awk -f myawk.awk /etc/passwd


----------------------------------------------------------
#求和
awk -F " " 'BEGIN { SUM=0 } { SUM = SUM + $5 } END{ print "总额为:" SUM}' ./ employee.txt

#求平均值一
BEGIN {
FS=":"
SUM=0
}
{
SUM = SUM + $2
}
END{
print "平均值为:" SUM / NR
}
#求平均值二
BEGIN {
FS=":"
SUM=0
NUM=0
}
{
# $2为数字时进行计算
if($2 !~ /[a-zA-Z]/){
SUM = SUM + $2
NUM = NUM + 1
}
print $0
}
END{
print "平均值为:" SUM / NUM
}

#求最大值
BEGIN {
FS=" "
MAX=0
}
{
if ($5 > MAX){
MAX = $5
}
}
END{
printf("最大值为:%d ", MAX)
}

awk -f test4.awk employee.txt
----------------------------------------------------------

1、统计各科修课人数
vi countstu.txt
xiaoA math 90
xiaoB chiese 23
xiaoD English 45
xiaoC jichu 76
xiaoM math 23
xiaoY chiese 12
xiaoK math 56
xiaoY math 23
xiaoQ math 34
xiaoY jichu 12
xiaoP jichu 100

vi countstu.awk
{
Number[$2]++
}
END{
for(course in Number)
printf("%10s %d ", course, Number[course] )
}
运行:
awk -f ./countstu.awk countstu.txt
-------------------------------------------------------
2、awk实现wordcount案例
vi words.txt
wo shi tiancai
wo bu shi tiancai que ding ma

运行方式一:
vi wordcount.awk
{
for (i = 1; i<=NF; i++)
freq[$i]++
}
END{
for (word in f req)
printf "%s%d ",word,freq[word]
}
运行:
awk -f wordcount.awk words.txt


运行方式二:
vi wordcount_awk.sh
#!/bin/sh
awk -F " " '{
for (i = 1; i<=NF; i++)
freq[$i]++
}
END{
for (word in freq)
printf "%s%d ",word,freq[word]
}' $1

chmod u+x wordcount_awk.sh
./wordcount_awk.sh words.txt

案例3:重写一个hdfs的重启动文件
#!/bin/bash
#get all process
all=`jps | grep -v Jps | awk -F " " '{print $1}'`
if [ -n "$all" ] ; then
echo 'found hadoop process is running...'
jps | grep -v Jps | awk -F ' ' '{print "shutdown proccess > " $2}'
kill -9 `jps | grep -v Jps | awk -F ' ' '{print $1}'`
echo 'restart process that you need...'
if [ -f "$HADOOP_HOME/sbin/hadoop-daemon.sh" ]; then
#/opt/bigdata/hadoop-2.7.1/sbin/start-dfs.sh
$HADOOP_HOME/sbin/hadoop-daemon.sh start namenode
$HADOOP_HOME/sbin/hadoop-daemon.sh start datanode
$HADOOP_HOME/sbin/hadoop-daemon.sh start secondarynamenode
fi
else
echo 'not found hadoop process is running...'
echo 'start process that you need...'
if [ -f "$HADOOP_HOME/sbin/hadoop-daemon.sh" ]; then
#/opt/bigdata/hadoop-2.7.1/sbin/start-dfs.sh
$HADOOP_HOME/sbin/hadoop-daemon.sh start namenode
$HADOOP_HOME/sbin/hadoop-daemon.sh start datanode
$HADOOP_HOME/sbin/hadoop-daemon.sh start secondarynamenode
fi
fi

原文地址:https://www.cnblogs.com/lixin1101/p/7056389.html