大資料學習第二天——shell程式設計
2.1 基本格式
程式碼寫在普通文字檔案中,通常以 .sh為字尾名
vi hello.sh
#!/bin/bash ## 表示用哪一種shell解析器來解析執行我們的這個指令碼程式 echo "hello world" ## 註釋也可以寫在這裡 ## 這是一行註釋 |
執行指令碼
sh hello.sh
或給指令碼新增x許可權,直接執行
chmod 755 hello.sh
./hello.sh
2.2 基本語法
2.2.1 系統變數
Linux Shell中的變數分為“系統變數”和“使用者自定義變數”
可以通過set命令檢視系統變數
系統變數:$HOME、$PWD、$SHELL、$USER等等
2.2.2 自定義變數
1、語法
變數=值 (例如STR=abc)
等號兩側不能有空格
變數名稱一般習慣為大寫
使用變數: $arg
雙引號和單引號有區別,
雙引號僅將空格脫意,
單引號會將變數引用比如$param脫意
2、示例
STR="hello world"
A=9
echo $A
echo $STR
如果想列印 hello worlds is greater 怎麼辦?
echo $STRs is greate 行嗎?
不行,正確寫法是:
echo ${STR}s is greate
unset A 撤銷變數 A
readonly B=2 宣告靜態的變數 B=2,不能 unset
export A #可把變數提升為當前shell程序中的全域性環境變數,可供其他子shell程式使用
注意理解
[[email protected] scripts]# vi a.sh #!/bin/bash a="a in a.sh" echo $a /root/scripts/b.sh |
[[email protected] scripts]# vi b.sh #!/bin/bash b="b in b.sh" echo $b echo $a |
然後執行 ./a.sh ,會發現 b指令碼中並沒有把a指令碼中定義的a變數打印出來
如果要在b中打印出a指令碼的變數a,需要在a指令碼中把變數a做export定義
此時,a變數就成了a.sh指令碼所在bash程序的全域性變數,該程序的所有子程序都能訪問到變數a
另一種方式:
如果在a.sh指令碼中用如下方式呼叫b.sh
. ./b.sh ## 注意:重點關注最前面那個 “.”號 |
或者
source ./b.sh ## |
則,b.sh就在a.sh所在的bash程序空間中執行
總結:
1、a.sh中直接呼叫b.sh,會讓b.sh在a所在的bash程序的“子程序”空間中執行
2、而子程序空間只能訪問父程序中用export定義的變數
3、一個shell程序無法將自己定義的變數提升到父程序空間中去
4、“.”號執行指令碼時,會讓指令碼在呼叫者所在的shell程序空間中執行
3、反引號賦值
A=`ls -la` ## 反引號,執行裡面的命令,並把結果返回給變數A
A=$(ls -la) ## 等價於反引號
4、特殊變數
$? 表示上一個命令退出的狀態碼
$$ 表示當前程序編號
$0 表示當前指令碼名稱
$n 表示n位置的輸入引數(n代表數字,n>=1)
$# 表示引數的個數,常用於迴圈
$*和[email protected] 都表示引數列表 .`
注:$*與[email protected]區別
$* 和 [email protected] 都表示傳遞給函式或指令碼的所有引數
ü 不被雙引號" "包含時——
$* 和 [email protected] 都以$1 $2 … $n 的形式組成引數列表
ü 當它們被雙引號" "包含時——
"$*" 會將所有的引數作為一個整體,以"$1 $2 … $n"的形式組成一個整串;
"[email protected]" 會將各個引數分開,以"$1" "$2" … "$n" 的形式組成一個引數列表
2.3 運算子
2.3.1 算數運算
1、用expr
格式 expr m + n 或$((m+n)) 注意expr運算子間要有空格
例如計算(2+3 )×4 的值
1 .分步計算
S=`expr 2 + 3`
expr $S \* 4 ## *號需要轉義
2.一步完成計算
expr `expr 2 + 3 ` \* 4
echo `expr \`expr 2 + 3\` \* 4`
2、用(())
((1+2))
(((2+3)*4))
count=1
((count++))
echo $count
但是要想取到運算結果,需要用$引用
a=$((1+2))
3、用$[]
a=$[1+2]
echo $a
2.5 流程控制
2.5.1 if語法
1、語法格式
if condition
then
statements
[elif condition
then statements. ..]
[else
statements ]
fi
2、示例
#!/bin/bash read -p "please input your name:" NAME ## read命令用於從控制檯讀取輸入資料 ## printf '%s\n' $NAME if [ $NAME = root ] then echo "hello ${NAME}, welcome !" elif [ $NAME = itcast ] then echo "hello ${NAME}, welcome !" else echo "SB, get out here !" fi
|
3、判斷條件
1/ 條件判斷基本語法
[ condition ] (注意condition前後要有空格)
#非空返回true,可使用$?驗證(0為true,>1為false)
[ itcast ]
#空返回false
[ ]
注意[ ]內部的=周邊的空格,有區別:
[[email protected] scripts]# if [ a = b ];then echo ok;else echo notok;fi notok [[email protected] scripts]# if [ a=b ];then echo ok;else echo notok;fi ok |
短路(理解為三元運算子)
[ condition ] && echo OK || echo notok
條件滿足,執行&&後面的語句;條件不滿足,執行|| 後面的語句
2/ 條件判斷組合
注:[] 與[[ ]] 的區別:[[ ]] 中邏輯組合可以使用 && || 符號
而[] 裡面邏輯組合可以用 -a -o
[[email protected] ~]# if [ a = b && b = c ]; then echo ok;else echo notok;fi -bash: [: missing `]' notok
[[email protected] ~]# if [ a = b -a b = b ]; then echo ok;else echo notok;fi notok [[email protected] ~]# if [ a = b -o b = b ]; then echo ok;else echo notok;fi ok
[[email protected] ~]# if [[ a = b && b = b ]]; then echo ok;else echo notok;fi notok [[email protected] ~]# if [[ a = b || b = b ]]; then echo ok;else echo notok;fi ok |
3/ 常用判斷運算子
字串比較:= !=
-z 字串長度是為0返回true
-n 字串長度是不為0返回true
if [ 'aa' = 'bb' ]; then echo ok; else echo notok;fi
if [ -n "aa" ]; then echo ok; else echo notok;fi
if [ -z "" ]; then echo ok; else echo notok;fi
整數比較:
-lt 小於
-le 小於等於
-eq 等於
-gt 大於
-ge 大於等於
-ne 不等於
檔案判斷:
-d 是否為目錄
if [ -d /bin ]; then echo ok; else echo notok;fi
-f 是否為檔案
if [ -f /bin/ls ]; then echo ok; else echo notok;fi
-e 是否存在
if [ -e /bin/ls ]; then echo ok; else echo notok;fi
2.5.2 while語法
1、方式一
while expression
do
command
…
done
2、方式二
i=1
while ((i<=3))
do
echo $i
let i++
done
2.5.3 case語法
case $1 in
start)
echo "starting"
;;
stop)
echo "stoping"
;;
*)
echo "Usage: {start|stop}"
esac
2.5.4 for語法
1、方式一
for N in 1 2 3
do
echo $N
done
或
for N in 1 2 3; do echo $N; done
或
for N in {1..3}; do echo $N; done
2、方式二
for ((i = 0; i <= 5; i++))
do
echo "welcome $i times"
done
或
for ((i = 0; i <= 5; i++)); do echo "welcome $i times"; done
2.6 函式使用
2.6.1 函式定義
#!/bin/sh # func1.sh hello() ## 函式定義 { echo "Hello there today's date is `date +%Y-%m-%d`" # return 2 ###返回值其實是狀態碼,只能在[0-255]範圍內 } hello # echo $? 獲取函式的return值 echo "now going to the function hello" echo "back from the function" |
函式呼叫:
function hello()
或 function hello
或 hello
注意:
1.必須在呼叫函式地方之前,先宣告函式,shell指令碼是逐行執行。不會像其它語言一樣先預編譯
2.函式返回值,只能通過$? 系統變數獲得,可以顯示加:return 返回,如果不加,將以最後一條命令執行結果,作為返回值。 return後跟數值n(0-255)
指令碼除錯:
sh -vx helloWorld.sh
或者在指令碼中增加set -x
2.6.2 函式引數
#!/bin/bash # fun1.sh
funWithParam(){ echo "第一個引數為 $1 !" echo "第二個引數為 $2 !" echo "第十個引數為 $10 !" echo "第十個引數為 ${10} !" echo "第十一個引數為 ${11} !" echo "引數總數有 $# 個!" echo "作為一個字串輸出所有引數 $* !" } funWithParam 1 2 3 4 5 6 7 8 9 34 73 |
注意,$10 不能獲取第十個引數,獲取第十個引數需要${10}。當n>=10時,需要使用${n}來獲取引數。
2.6.3 函式返回值
#!/bin/bash # fun2.sh funWithReturn(){ echo "這個函式會對輸入的兩個數字進行相加運算..." echo "輸入第一個數字: " read aNum echo "輸入第二個數字: " read anotherNum echo "兩個數字分別為 $aNum 和 $anotherNum !" return $(($aNum+$anotherNum)) } funWithReturn echo "輸入的兩個數字之和為 $? !" |
2.6.4 跨指令碼呼叫函式
假如上述的指令碼檔案fun2.sh儲存在此路徑: /root/fun2.sh
則可在指令碼fun_other.sh中呼叫指令碼fun2.sh中的函式
#!/bin/bash # fun_other.sh . /root/fun2.sh ## 注: . 和 / 之間有空格 # 或者 source /root/fun2.sh funWithParam 11 22 33 44 55 66 77 88 99 100 101
|
03/ shell程式設計綜合練習
自動化軟體部署指令碼
3.1 需求
1、需求描述
公司內有一個N個節點的叢集,需要統一安裝一些軟體(jdk)
需要開發一個指令碼,實現對叢集中的N臺節點批量自動下載、安裝jdk
2、思路
1/ 編寫一個啟動指令碼,用來發送一個軟體安裝指令碼到每一臺機器
2/ 然後啟動每臺機器上的軟體安裝指令碼來執行軟體下載和安裝
3、expect的使用
痛點:使用scp命令遠端拷貝檔案時,會有人機互動的過程,如何讓指令碼完成人機互動?
妙藥: expect
用法示例:
先觀察 ssh localhost 的過程
再看expect的功能
#!/bin/bash/expect ## exp_test.sh set timeout -1; spawn ssh localhost; expect { "(yes/no)" {send "yes\r";exp_continue;} "password:" {send "hadoop\r";exp_continue;} eof {exit 0;} } |
執行: expect -f exp_test.sh
3.2 準備內網軟體下載伺服器
選擇一臺伺服器(比如mini)作為軟體源伺服器
1、安裝httpd (如果已有,可跳過)
yum install -y httpd
service httpd start
chkconfig --level 35 httpd on
2、製作區域網yum源
1/ 掛載centos安裝光碟到/mnt/cdrom (如果已有,可跳過)
mkdir /mnt/cdrom
mount -t iso9660 -o loop /dev/cdrom /mnt/cdrom
2/ 將本地yum庫放入httpd伺服器
ln -s /mnt/cdrom /var/www/html/centos
檢查點:用瀏覽器訪問 http://mini/centos 看能否看到光碟內容
3、編寫repo配置
vi /etc/yum.repos.d/centos.repo
[c6-httpd] name=CentOS-httpd baseurl=http://192.168.33.3/centos gpgcheck=0 enabled=1 |
4、分發repo配置到區域網
從母雞shizhan01上把centos.repo拷貝給所有需要自動安裝軟體的伺服器(仔雞mini1/mini2)
cd /etc/yum.repos.d/
scp /etc/yum.repos.d/innet.repo mini1:$PWD
scp /etc/yum.repos.d/innet.repo mini2:$PWD
5、準備一個jdk安裝包放在內網web伺服器上
3.3 指令碼開發
1、啟動指令碼
vi boot.sh
#!/bin/bash
SERVERS="mini1 mini2" PASSWORD=hadoop BASE_SERVER=192.168.33.11
## 實現免密登陸配置的函式 auto_ssh_copy_id() { expect -c "set timeout -1; spawn ssh-copy-id $1; expect { *(yes/no)* {send -- yes\r;exp_continue;} *assword:* {send -- $2\r;exp_continue;} eof {exit 0;} }"; }
ssh_copy_id_to_all() { for SERVER in $SERVERS do auto_ssh_copy_id $SERVER $PASSWORD done }
## 呼叫免密登陸配置函式,實現母雞到各仔雞的免密登陸配置 ssh_copy_id_to_all
## 完成分發install.sh到各仔雞的操作 ## 並讓仔雞啟動install.sh for SERVER in $SERVERS do scp install.sh [email protected]$SERVER:/root ssh [email protected]$SERVER /root/install.sh done |
2、安裝執行指令碼
vi install.sh
#!/bin/bash
BASE_SERVER=192.168.33.11 ## 為本機安裝wget命令 yum install -y wget ## 使用wget從母雞的web伺服器上下載jdk壓縮包 wget $BASE_SERVER/soft/jdk-7u67-linux-x64.gz ## 將下載的壓縮包解壓 tar -zxvf jdk-7u67-linux-x64.gz -C /usr/local ## 修改profile配置檔案 cat >> /etc/profile << EOF export JAVA_HOME=/usr/local/jdk1.7.0_67 export PATH=\$PATH:\$JAVA_HOME/bin EOF |
3、啟動指令碼
只要在baseServer即mini上啟動boot.sh即可