shell腳本基礎(chǔ)
shell腳本可以輸入多個(gè)命令并處理每個(gè)命令的結(jié)果。
創(chuàng)建shell腳本時(shí),需要在文件的第一行指定使用的解析器(#!/bin/bash)
通常shell腳本在行里,#號(hào)用作注釋行,不被執(zhí)行,第一行是個(gè)特例,它指定執(zhí)行的解析器
vim 第一個(gè)腳本文件.sh
#!/bin/bash
# 這是一個(gè)demo
date
wc file1
# 執(zhí)行腳本
sh ***.sh
./***.sh
特殊符號(hào)
-
反引號(hào)
反引號(hào)(``):允許你把shell變量的結(jié)果輸出賦值給變量
test=`date` echo $test #顯示的是時(shí)間 test=`echo "hello world"` echo $test -
重定向
重定向符號(hào)指向數(shù)據(jù)流動(dòng)的方向。
重定向(>,>>):允許你將某個(gè)命令的輸入輸出重定向到另一個(gè)位置
date > file # 覆蓋原有數(shù)據(jù) date >> file # 追加數(shù)據(jù)輸入重定向(<):將文件內(nèi)容重定向到命令中
> wc < file > wc <<aa > ok > world > aa?
-
管道
取代輸出重定向到文件件,可以將重定向到另一個(gè)命令,稱為管道連接。
管道連接不是一個(gè)一個(gè)運(yùn)行的,在系統(tǒng)內(nèi)部將他們連接起來,一個(gè)命令輸出的同時(shí),另一個(gè)命令同時(shí)運(yùn)行,傳輸數(shù)據(jù)不會(huì)用的任何中間數(shù)據(jù)和中間文件
ls > file sort < file ls | sort
數(shù)學(xué)運(yùn)算相關(guān)命令
expr:用來處理數(shù)學(xué)表達(dá)式,它可以識(shí)別一些不同的數(shù)字和字符串操作(操作符兩側(cè)的空格及轉(zhuǎn)移)


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 #設(shè)置行號(hào)
#數(shù)學(xué)表達(dá)式簡單方法 $[operation]
var2=$[5+2]
echo $var2
shell腳本(條件語句)
if語句運(yùn)行if定義命令(如果退出狀態(tài)嗎為0,運(yùn)行then部分命令)
vim test.sh
if date
then
#語句塊
echo "running then"
echo "---------------"
fi
sh test.sh
#多命令用;分割
echo $var1;echo $var2
date
echo $? #上一個(gè)狀態(tài)退出碼
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命令(數(shù)值比較,字符串比較,文件比較)
格式1:test condition
格式2:[ condition ] (左右空格一般都要個(gè)加一個(gè)空格,否則會(huì)報(bào)錯(cuò))

#!/bin/bash
########################################## ##### **********
##### **********
#########################################
if test 6 -gt 5
then
echo "6 > 5"
fi
if [ 6 -gt 5 ]
then
echo "6 > 5"
fi
#不能處理小數(shù)

- test的比較會(huì)將所有的標(biāo)點(diǎn)和大寫也考慮在內(nèi)。
- 要轉(zhuǎn)義大于小于號(hào),否則會(huì)被當(dāng)為重定向符號(hào)
- test命令字符串比較會(huì)使用標(biāo)準(zhǔn)的ASCII順序,根據(jù)每個(gè)字符的ASCII數(shù)值來決定排序順序。
- test命令使用標(biāo)準(zhǔn)的數(shù)學(xué)比較符號(hào)來表示字符串的比較,而用文本代碼表示數(shù)值比較。
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
復(fù)合條件
if date && [ -n aaa ]
then
echo "run"
else
echo "no run"
fi
if-then語句高級(jí)特性
用于數(shù)學(xué)表達(dá)式的雙括號(hào)(空格可以省略,不需要轉(zhuǎn)義大于小于等符號(hào))
(( ))雙括號(hào)命令允許你將高級(jí)數(shù)學(xué)表達(dá)式放入比較中,test命令只允許在比較中進(jìn)行簡單的算術(shù)操作-
用于高級(jí)字符串處理功能的雙方括號(hào)(兩側(cè)空格必須存在)
[[ ]] 雙方括號(hào)支持test命令的特性,并新增了另一個(gè)特性模式(正則表達(dá)式)匹配.
image.png
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命令會(huì)檢查單個(gè)變量列表格式的多個(gè)值.
- 通過豎線操作符來分割模式
- 星號(hào)*會(huì)捕獲所有跟列出的模式都不匹配的值
- 每一個(gè)語句塊一雙分號(hào)結(jié)尾
case $USER in
work333 | work1)
echo "welcome work work1";;
work2)
echo "welcome work2";;
*)
echo "welcome other";;
esac
shell腳本(循環(huán)語句for,while,until)
特殊的環(huán)境變量IFS, 稱為內(nèi)部字段分隔符(interal field separator) ,IFS環(huán)境變量定義了bash shell用作字段分隔符的一系列字符。>set |grep IFS 默認(rèn)情況下,bash shell會(huì)將空格、制表符、換行符定義為字段分隔符。
for test in Alabama Alaska Arizona California
do
echo "The next is $test"
done
echo $test #該值會(huì)在shell腳本的剩余部分一直保持有效
val="Alabama Alaska Arizona California"
for test in $val # 從變量讀取迭代列表
do
echo "The next is $test"
done
# 注意單引號(hào)的轉(zhuǎn)義,及元素中帶空格的處理
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語言風(fēng)格的for循環(huán)
do
echo "The next number is $i"
done
for ((a=1,b=10;a<=10,b>2;a++,b--)) #C語言風(fēng)格的多個(gè)條件
do
echo "$a - $b"
done
while命令:
允許你在while語句行定義多個(gè)測試命令,只有最后一個(gè)測試命令的退出狀態(tài)碼會(huì)被用來決定什么時(shí)候結(jié)束循環(huán)。
val1=10
while [ $val1 -gt 0 ]
do
echo $val1
(( val1-- ))
#val1=$[$val1-1]
done
val1=10
while echo $val1 #定義多個(gè)測試命令,只有最后一個(gè)測試命令的退出狀態(tài)碼會(huì)被用來決定結(jié)束循環(huán)
[ $val1 -gt 0 ]
do
echo "This is flag"
val1=$[$val1-1]
done
until命令:要求你指定一個(gè)通常輸出非零退出狀態(tài)碼的測試命令,一旦測試命令返回退出狀態(tài)碼0,則循環(huán)就結(jié)束了。
val1=10
until [ $val1 -eq 0 ] #until也是支持同時(shí)指定多個(gè)命令的
do
echo $val1
((val1--))
done
嵌套循環(huán):
for (( a=1;a<=3;a++ )) #嵌套循環(huán)
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 #跳出循環(huán)
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 #跳出最靠近的循環(huán)
fi
echo " Inner loop: $b"
done
done
echo "----------"
#跳出外部循環(huán)
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要跳出的層級(jí),若不指明n則 默認(rèn)層級(jí)為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設(shè)置提早結(jié)束執(zhí)行循環(huán)內(nèi)部的命令,但并不終止循環(huán)
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 #指定要繼續(xù)的循環(huán)層級(jí)n
fi
var3=$[ $a*$b ]
echo " The result of $a * $b is $var3"
done
done
shell腳本高級(jí)部分(函數(shù)定義,函數(shù)參數(shù),函數(shù)變量)
函數(shù)是可以起個(gè)名字并在代碼中任何位置重用的代碼塊。你要在腳本中使用該代碼塊時(shí),只要使用分配的函數(shù)名就行了
- 函數(shù)定義要在使用函數(shù)前
- 如果你重定義了函數(shù),新定義會(huì)覆蓋原來函數(shù)的定義,而不會(huì)產(chǎn)生任何錯(cuò)誤消息。
- 默認(rèn)情況下,函數(shù)的退出狀態(tài)碼是函數(shù)中最后一條命令返回的退出狀態(tài)碼
function f1 { #function定義的函數(shù) 函數(shù)名和花括號(hào)之間必須加入空格,否則報(bào)錯(cuò)
echo "This is an example of a function"
}
f2(){
echo "This is second example"
}
for ((a=1;a<10;a++))
do
f1 #注意函數(shù)定義要在使用函數(shù)前
f2
done
$?的說明
$?變量會(huì)返回執(zhí)行的最后一條命令的退出狀態(tài)碼
可以在函數(shù)中指定return返回的退出狀態(tài)碼,要注意退出狀態(tài)碼的值必須在0~255之間, 任何大于256的值都會(huì)返回一個(gè)錯(cuò)誤的值
f3(){
echo "f3"
ls badfile #函數(shù)的退出狀態(tài)碼是函數(shù)中最后一條命令返回的退出狀態(tài)碼
}
f3
echo "the function f3 returncode="$?
f4(){
var1=3
return $[$var1 * 3] #return指定函數(shù)的退出狀態(tài)碼
}
f4
echo $?
參數(shù):函數(shù)使用標(biāo)準(zhǔn)的參數(shù)環(huán)境變量來代表命令行上傳給函數(shù)的參數(shù),函數(shù)名會(huì)在$0變量中定義,函數(shù)命令行上的任何參數(shù)都會(huì)通過
$1、$2等定義,也可以用特殊變量$#來判斷傳給函數(shù)的參數(shù)數(shù)目。
#向函數(shù)傳遞參數(shù) 與向腳本傳遞參數(shù)使用方式相同
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
函數(shù)中的變量:全局變量,局部變量
- 默認(rèn)情況下,在腳本中定義的任何變量都是全局變量,可以在函數(shù)內(nèi)正常訪問,如果全局變量在函數(shù)內(nèi)被賦予了新值,那么在腳本中引用該變量時(shí),新值依然有效。
- 在函數(shù)內(nèi)部聲明局部變量 ,只需要在變量前加入 local關(guān)鍵字。 局部變量保證了變量只局限在該函數(shù)中,如果腳本中在該函數(shù)之外有相同名字的變量,那么shell將會(huì)保持這兩個(gè)變量的值獨(dú)立。
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庫文件
在單個(gè)腳本中,使用函數(shù)可以省去一些鍵入的工作,但是在多個(gè)腳本之間如何使用同一個(gè)代碼塊呢,顯然,在每個(gè)腳本中都定義同樣的函數(shù)而只使用一次會(huì)比較麻煩。
- shell創(chuàng)建庫文件
- shell函數(shù)的作用域:shell只對(duì)定義它的shell會(huì)話有效。
- source會(huì)在當(dāng)前的shell上下文中執(zhí)行命令,而不是創(chuàng)建一個(gè)新的shell來執(zhí)行命令。
- source命令有個(gè)快捷別名,稱作點(diǎn)操作符 .
vim init.sh
#!/bin/bash
f(){
echo "okokokok"
}
vim test7.sh
#!/bin/bash
f
source init.sh #可以全路徑引用
f
vim test7.sh
#!/bin/bash
f #因?yàn)樽饔糜騿栴},會(huì)報(bào)錯(cuò)
. init.sh
f
exit #退出腳本命令
工作中常用小工具
防止誤刪工具(本質(zhì)是移動(dòng)目錄)
vim ~/.bashrc
trash(){
mv $1 ~/Trash1
}
alias rm="trash"
批量修改數(shù)據(jù)并保存日志
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
檢查文件數(shù)(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
