shell脚本基础
shell脚本可以输入多个命令并处理每个命令的结果。
创建shell脚本时,需要在文件的第一行指定使用的解析器(#!/bin/bash)
通常shell脚本在行里,#号用作注释行,不被执行,第一行是个特例,它指定执行的解析器
vim 第一个脚本文件.sh
#!/bin/bash
# 这是一个demo
date
wc file1
# 执行脚本
sh ***.sh
./***.sh
特殊符号
-
反引号
反引号(``):允许你把shell变量的结果输出赋值给变量
test=`date` echo $test #显示的是时间 test=`echo "hello world"` echo $test
-
重定向
重定向符号指向数据流动的方向。
重定向(>,>>):允许你将某个命令的输入输出重定向到另一个位置
date > file # 覆盖原有数据 date >> file # 追加数据
输入重定向(<):将文件内容重定向到命令中
> wc < file > wc <<aa > ok > world > aa
-
管道
取代输出重定向到文件件,可以将重定向到另一个命令,称为管道连接。
管道连接不是一个一个运行的,在系统内部将他们连接起来,一个命令输出的同时,另一个命令同时运行,传输数据不会用的任何中间数据和中间文件
ls > file sort < file ls | sort
数学运算相关命令
expr:用来处理数学表达式,它可以识别一些不同的数字和字符串操作(操作符两侧的空格及转移)
expr 1 + 5
expr 1 \* 5
vim test.sh
#!/bin/bash #指定解析器
var1=3
var2=2
var3=`expr $var1 \* $var2`
echo $var3
sh test.sh
chmod 777 test.sh
./test.sh
echo $PATH
vim ~/.vimrc
set number #设置行号
#数学表达式简单方法 $[operation]
var2=$[5+2]
echo $var2
shell脚本(条件语句)
if语句运行if定义命令(如果退出状态吗为0,运行then部分命令)
vim test.sh
if date
then
#语句块
echo "running then"
echo "---------------"
fi
sh test.sh
#多命令用;分割
echo $var1;echo $var2
date
echo $? #上一个状态退出码
if ssss
then
#语句块
echo "running then"
echo "---------------"
else
#语句块
echo 'runing else'
fi
if ssss
then
#语句块
echo "running then"
echo "---------------"
elif date
then
echo "else"
else
#语句块
echo 'runing else'
fi
test命令(数值比较,字符串比较,文件比较)
格式1:test condition
格式2:[ condition ] (左右空格一般都要个加一个空格,否则会报错)
#!/bin/bash
########################################## ##### **********
##### **********
#########################################
if test 6 -gt 5
then
echo "6 > 5"
fi
if [ 6 -gt 5 ]
then
echo "6 > 5"
fi
#不能处理小数
- test的比较会将所有的标点和大写也考虑在内。
- 要转义大于小于号,否则会被当为重定向符号
- test命令字符串比较会使用标准的ASCII顺序,根据每个字符的ASCII数值来决定排序顺序。
- test命令使用标准的数学比较符号来表示字符串的比较,而用文本代码表示数值比较。
12 if [ aa = aa ]
13 then
14 echo "equal"
15 fi
16
17 user=aaaa
18 testuser=aaaa
19 if [ $user != $testuser ]
20 then
21 echo "no eque"
22 else
23 echo "eque"
24 fi
if [ aaa \> bbb ]
then
echo "aaaa>bbbb"
fi
var1=""
if [ -z var1 ]
then
echo "是空"
fi
27 if [ -d $HOME ]
28 then
29 ls $HOME
30 if [ -r $HOME ]
31 then
32 echo "$HOME is read"
33 fi
34 else
35 echo "is not directory"
36 fi
复合条件
if date && [ -n aaa ]
then
echo "run"
else
echo "no run"
fi
if-then语句高级特性
用于数学表达式的双括号(空格可以省略,不需要转义大于小于等符号)
(( ))双括号命令允许你将高级数学表达式放入比较中,test命令只允许在比较中进行简单的算术操作-
用于高级字符串处理功能的双方括号(两侧空格必须存在)
[[ ]] 双方括号支持test命令的特性,并新增了另一个特性模式(正则表达式)匹配.
if ((10 ** 2 >90));then
((v2= 10 ** 2))
echo "v2="$v2
fi
if [[ $USER = w* ]];then
echo "yes"
fi
case命令
- 为了避免在一组可能的值中寻找特定值而写出很长的if-then-else语句,引入case命令
- case命令会检查单个变量列表格式的多个值.
- 通过竖线操作符来分割模式
- 星号*会捕获所有跟列出的模式都不匹配的值
- 每一个语句块一双分号结尾
case $USER in
work333 | work1)
echo "welcome work work1";;
work2)
echo "welcome work2";;
*)
echo "welcome other";;
esac
shell脚本(循环语句for,while,until)
特殊的环境变量IFS, 称为内部字段分隔符(interal field separator) ,IFS环境变量定义了bash shell用作字段分隔符的一系列字符。>set |grep IFS
默认情况下,bash shell会将空格、制表符、换行符定义为字段分隔符。
for test in Alabama Alaska Arizona California
do
echo "The next is $test"
done
echo $test #该值会在shell脚本的剩余部分一直保持有效
val="Alabama Alaska Arizona California"
for test in $val # 从变量读取迭代列表
do
echo "The next is $test"
done
# 注意单引号的转义,及元素中带空格的处理
for test in I\'am a boy, "you are a gril"
do
echo "The next is $test"
done
#从文件读取迭代列表
for test in `cat bb`;
do
echo "The next is $test"
done
for ((i=0;i<10;i++)) #C语言风格的for循环
do
echo "The next number is $i"
done
for ((a=1,b=10;a<=10,b>2;a++,b--)) #C语言风格的多个条件
do
echo "$a - $b"
done
while命令:
允许你在while语句行定义多个测试命令,只有最后一个测试命令的退出状态码会被用来决定什么时候结束循环。
val1=10
while [ $val1 -gt 0 ]
do
echo $val1
(( val1-- ))
#val1=$[$val1-1]
done
val1=10
while echo $val1 #定义多个测试命令,只有最后一个测试命令的退出状态码会被用来决定结束循环
[ $val1 -gt 0 ]
do
echo "This is flag"
val1=$[$val1-1]
done
until命令:要求你指定一个通常输出非零退出状态码的测试命令,一旦测试命令返回退出状态码0,则循环就结束了。
val1=10
until [ $val1 -eq 0 ] #until也是支持同时指定多个命令的
do
echo $val1
((val1--))
done
嵌套循环:
for (( a=1;a<=3;a++ )) #嵌套循环
do
echo "start loop $a"
for ((b=1;b<=3;b++))
do
echo "Inside loop: $b"
done
done
break和continue命令
for var1 in 1 2 3 4 5 6 7
do
if [ $var1 -eq 5 ]
then
break #跳出循环
fi
echo "echo $var1"
done
var1=10
while [ $var1 -gt 0 ]
do
if [ $var1 -eq 5 ]
then
break
fi
echo "while echo $var1"
((var1--))
done
for ((a=1;a<4;a++))
do
echo "Outer loop :$a"
for ((b=1;b<100;b++))
do
if [ $b -eq 5 ];then
break #跳出最靠近的循环
fi
echo " Inner loop: $b"
done
done
echo "----------"
#跳出外部循环
for ((a=1;a<4;a++))
do
echo "Outer loop :$a"
for ((b=1;b<100;b++))
do
if [ $b -eq 5 ];then
break 2 #n指明break要跳出的层级,若不指明n则 默认层级为1
fi
echo " Inner loop: $b"
done
done
for ((var1=1;var1<15;var1++))
do
if [ $var1 -gt 5 ] && [ $var1 -lt 10 ]
then
continue #continue设置提早结束执行循环内部的命令,但并不终止循环
fi
echo "Iteration number: $var1"
done
for ((a=1;a<=5;a++))
do
echo "Iteration $a"
for ((b=1;b<3;b++))
do
echo " b= $b"
if [ $a -gt 2 ] && [ $a -lt 4 ]
then
continue 2 #指定要继续的循环层级n
fi
var3=$[ $a*$b ]
echo " The result of $a * $b is $var3"
done
done
shell脚本高级部分(函数定义,函数参数,函数变量)
函数是可以起个名字并在代码中任何位置重用的代码块。你要在脚本中使用该代码块时,只要使用分配的函数名就行了
- 函数定义要在使用函数前
- 如果你重定义了函数,新定义会覆盖原来函数的定义,而不会产生任何错误消息。
- 默认情况下,函数的退出状态码是函数中最后一条命令返回的退出状态码
function f1 { #function定义的函数 函数名和花括号之间必须加入空格,否则报错
echo "This is an example of a function"
}
f2(){
echo "This is second example"
}
for ((a=1;a<10;a++))
do
f1 #注意函数定义要在使用函数前
f2
done
$?的说明
$?变量会返回执行的最后一条命令的退出状态码
可以在函数中指定return返回的退出状态码,要注意退出状态码的值必须在0~255之间, 任何大于256的值都会返回一个错误的值
f3(){
echo "f3"
ls badfile #函数的退出状态码是函数中最后一条命令返回的退出状态码
}
f3
echo "the function f3 returncode="$?
f4(){
var1=3
return $[$var1 * 3] #return指定函数的退出状态码
}
f4
echo $?
参数:函数使用标准的参数环境变量来代表命令行上传给函数的参数,函数名会在$0变量中定义,函数命令行上的任何参数都会通过
$1、$2等定义,也可以用特殊变量$#来判断传给函数的参数数目。
#向函数传递参数 与向脚本传递参数使用方式相同
addem(){
if [ $# -eq 0 ] || [ $# -gt 2 ]
then
echo -1
elif [ $# -eq 1 ]
then
echo $1
else
echo $[$1+$2]
fi
}
addem 8 9
addem 10
addem
函数中的变量:全局变量,局部变量
- 默认情况下,在脚本中定义的任何变量都是全局变量,可以在函数内正常访问,如果全局变量在函数内被赋予了新值,那么在脚本中引用该变量时,新值依然有效。
- 在函数内部声明局部变量 ,只需要在变量前加入 local关键字。 局部变量保证了变量只局限在该函数中,如果脚本中在该函数之外有相同名字的变量,那么shell将会保持这两个变量的值独立。
f6(){
temp=$[$value+5]
}
temp=4
value=6
f6
if [ $temp -gt $value ];then
echo "temp is large"
else
echo "temp is smaller"
fi
f7(){
local temp=$[$value+5]
}
temp=4
value=6
f7
if [ $temp -gt $value ];then
echo "temp is large"
else
echo "temp is smaller"
fi
shell库文件
在单个脚本中,使用函数可以省去一些键入的工作,但是在多个脚本之间如何使用同一个代码块呢,显然,在每个脚本中都定义同样的函数而只使用一次会比较麻烦。
- shell创建库文件
- shell函数的作用域:shell只对定义它的shell会话有效。
- source会在当前的shell上下文中执行命令,而不是创建一个新的shell来执行命令。
- source命令有个快捷别名,称作点操作符 .
vim init.sh
#!/bin/bash
f(){
echo "okokokok"
}
vim test7.sh
#!/bin/bash
f
source init.sh #可以全路径引用
f
vim test7.sh
#!/bin/bash
f #因为作用域问题,会报错
. init.sh
f
exit #退出脚本命令
工作中常用小工具
防止误删工具(本质是移动目录)
vim ~/.bashrc
trash(){
mv $1 ~/Trash1
}
alias rm="trash"
批量修改数据并保存日志
vim exe_targetday.sh
#!/bin/bash
set -x
source init.sh
startDay=$1
endDay=$2
if [ -z ${startDay} ]
then startDay=`date -d "-1 day" +"%Y%m%d"`
fi
if [ -z ${endDay} ]
then endDay=${startDay}
fi
while [ ${startDay} -le ${endDay} ]
do
echo ${startDay}
xqe -e "use namespace fsg_ns;alter table base_data.wappass_udi drop if exists partition(dt='${startDay}');alter table base_data.wappass_udi add partition(dt='${startDay}') location 'afs://xingtian.afs.baidu.com:9902/app/passport_log/queryengine/pass_data/wappass_udi/dt=${startDay}';" &> ${startDay}.log &
startDay=`date -d "${startDay} 1 day" +"%Y%m%d"`
done
wait
echo "ok"
sh exe_targetday.sh 20180506 20180507
检查文件数(check_file_num.sh)
#! /bin/sh
#
# check_file_num.sh
# Copyright (C) 2017 news_dl <news_dl@launcher-news-dl.hadoop.busdev.usw2.cmcm.com>
#
# Distributed under terms of the MIT license.
# ./check_file_num.sh s3://com.cmcm.instanews.usw2.prod/deeplearning/huangyanyan/trash/data/id_convert_path 20170309 20170322 102
targetPath=$1
startDay=$2
endDay=$3
fileNum=$4
while [ ${startDay} -le ${endDay} ]
do
num=`hadoop fs -ls ${targetPath}/$startDay|wc -l`
echo "startDay="$startDay",num="$num
if [ "$num" -eq "$fileNum" ]
then
echo "ok"
else
echo "no"
exit 1
fi
startDay=`date -d "${startDay} 1 day" +"%Y%m%d"`
done