1. 程式人生 > >大資料學習第二天——shell程式設計

大資料學習第二天——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程式使用

注意理解

export

[[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指令碼中把變數aexport定義

此時,a變數就成了a.sh指令碼所在bash程序的全域性變數,該程序的所有子程序都能訪問到變數a

 

 

另一種方式:

如果在a.sh指令碼中用如下方式呼叫b.sh

.  ./b.sh    ## 注意:重點關注最前面那個 “.”號

或者

source ./b.sh   ##

則,b.sh就在a.sh所在的bash程序空間中執行

 

 

總結:

1a.sh中直接呼叫b.sh,會讓b.sha所在的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運算子間要有空格

例如計算(23 )×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,可使用$?驗證(0true>1false

[ 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/ 然後啟動每臺機器上的軟體安裝指令碼來執行軟體下載和安裝

 

3expect的使用

痛點:使用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、啟動指令碼

只要在baseServermini上啟動boot.sh即可