Shell腳本的模塊化和腳本複用


  平常工做中編寫shell腳本是再日常不過的了,那編寫過這麼多的shell,當咱們碰見曾經寫過的相關的處理函數時,如何來複用?直接拷貝相應的代碼仍是「引用過來」,或者是在編寫一個功能相對複雜的shell時,如何作到模塊化,如何組織複雜的函數調用?docker

  如何導入複用和模塊化,就是這裏要一塊兒學習的。shell

咱們會按照如下幾個部分來學習: 編程

一、shell執行方式不一樣的效果不一樣bash

二、導入模塊編程語言


一、shell執行方式不一樣的效果不一樣ide

第一種:sh  test.sh模塊化

這種方式會在咱們當前進程中,再新建一個子進行去執行這個test.sh 腳本。咱們能獲取到的東西,也就是這個test.sh執行完的結果或者輸出。函數

 

root@docker-host-03:~#more test.sh 學習

#!/bin/bash測試

s="thisis a test file"

root@docker-host-03:~#sh test.sh

root@docker-host-03:~#echo $s

root@docker-host-03:~#sh test.sh

root@docker-host-03:~#echo $?

0

  第二種:sourcetest.sh  或者.  Test.sh 

  這種方式至關因而將test.sh的內容拷貝到當前進程中來執行。這樣咱們能獲取到的內容就不只僅是這個test.sh執行完成的結果。咱們還能獲取到在這個腳本中定義的全局變量,定義的功能函數。由於咱們是將腳本的內容拷貝到當前文件中。

    那這種方式就和咱們不少編程語言中的模塊導入相似。咱們來驗證下是否是。

root@docker-host-03:~#. test.sh

root@docker-host-03:~#echo $s  

this is atest file

root@docker-host-03:~#s=0

root@docker-host-03:~#echo $s

0

root@docker-host-03:~#source  test.sh

root@docker-host-03:~#echo $s        

this is a test file

#咱們能夠看到這裏,能夠獲取到變量s的內容。


二、導入模塊

  2.1)先導入一個最簡單的

  基於上面第一點咱們提到的兩種shell的執行方式中的第二種。咱們利用這種「拷貝」的特性就可以實現將之前寫過的功能,進行導入

   好比咱們寫了很簡單格式化輸出log的函數:

root@docker-host-03:~# cat test1.sh

#!/bin/bash

 

MODULENAME=$(basename $0)

LOGFILE=/tmp/logfile-`date +%Y%m%d`

 

log_info()

{

  #[2017-03-31 12:00:00 ] - TextName - The log message

  Localdatetime=`date "+%Y-%m-%d %H:%M:%S"`

  if[ "$1" ];then

    echo "[ ${datetime} ] - ${MODULENAME} - $1 " | tee -a${LOGFILE}

 else

   return 1

  fi

}


上面這個log_info 函數會格式化刷出咱們的日誌內容。咱們能夠在多處複用。

咱們這裏的Main.sh腳本和test1.sh在同一個目錄裏面。咱們main.sh先導入test1.sh的內容,而後使用test1.sh中已有的變量,功能函數。咱們來看下main.sh的內容。

root@docker-host-03:~# cat main.sh

#!/bin/bash

 

#咱們在這裏經過.  Test1.sh 或是source test1.sh的方式將test1.sh導入進來。

. test1.sh

main()

{

 #咱們在這裏就能夠直接使用log_info函數了。

 log_info 'This is the main shell'

}

main

root@docker-host-03:~# ./main.sh 

[ 2017-03-31 15:11:23 ] - main.sh - This isthe main shell

root@docker-host-03:~# more/tmp/logfile-20170331

[ 2017-03-31 15:11:39 ] - main.sh - This isthe main shell

 


  2.2)若是咱們須要從其餘地方導入其餘函數

上面咱們的maintest1是在同一個目錄中,因此咱們在main 中使用的是相對路徑。若是咱們須要導入的「模塊」test1.sh 是放在其餘的目錄?或者說咱們的模塊存在層級關係?咱們有應該如何來引入呢?

   

1)因咱們在引入的時候,系統默認會在環境變量$PATH中去尋找咱們要導入的文件。一種作法是咱們將咱們須要用到的公共模塊放到$PATH包含的路徑中。或者咱們添加一個公用的路徑到$PATH環境變量中,而後將咱們須要用到的公有模塊放到公用文件夾下。

如:

root@docker-host-03:~/test_shell# cat  main.sh

#!/bin/bash

 

. test1.sh

. Config.sh

main()

{

 log_info 'This is the main shell'

 readConf

 echo${conf}

}

main

root@docker-host-03:~/test_shell# echo$PATH

/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games

#在這裏咱們能夠看到咱們將兩個模塊放在了PATH的路徑下面。

root@docker-host-03:~/test_shell# ls/usr/local/bin/test1.sh

/usr/local/bin/test1.sh

root@docker-host-03:~/test_shell# ls -l/usr/local/bin/Config.sh

-rw-r--r-- 1 root root 59 Mar 31 15:41/usr/local/bin/Config.sh

 

 


這裏我直接將文件拷貝到了$PATH有包含的目錄中。

 

2)  咱們將模塊所在的絕對路徑exportPATH中:

:

root@docker-host-03:~/test_shell/module#pwd

/root/test_shell/module

root@docker-host-03:~/test_shell/module#tree

.

├── Config.sh

└── test1.sh

 

0 directories, 2 files

root@docker-host-03:~/test_shell/module# cd../

root@docker-host-03:~/test_shell# moremain.sh

#!/bin/bash

 

#這裏咱們給了一個絕對路徑

MODUPATH=/root/test_shell/module

export PATH=$PATH:${MODUPATH}

 

. test1.sh

. Config.sh

main()

{

 log_info 'This is the main shell'

 readConf

 echo${conf}

}

Main

 


3) 動態的加載模塊

固然若是咱們在編寫一個功能相對複雜的shell的時候,咱們將會將這個一個整個複雜的帶有結構關係的shell打包,拷貝到任何一個地方去執行。這樣咱們就沒有辦法事先知道咱們的絕對路徑是怎樣的。好比這樣的目錄結構:

root@docker-host-03:~/test_shell#pwd

/root/test_shell

root@docker-host-03:~/test_shell#tree

.

├── main.sh

├── module

   ├── Config.sh

   └── test1.sh

└── module2

    └── mail.sh

 

2directories, 4 files

root@docker-host-03:~/test_shell#more main.sh

#!/bin/bash

 

#這裏動態的獲取到咱們的絕對路徑,並把模塊的路徑拼湊出來。

MODUPATH=$(dirname$(readlink -f $0))/module

echo${MODUPATH}

exportPATH=$PATH:${MODUPATH}

 

.test1.sh

.Config.sh

main()

{

 log_info 'This is the main shell'

 readConf

 echo ${conf}

}

Main

 


4)如何避免重複屢次的導入某個模塊

 

咱們要說source,其實就是將內容拷貝到同一個地方執行,那咱們能夠在模塊中加入一個判斷條件。若是發現咱們模塊中的某個變量已經存在,咱們就return出去,再也不導入後續的內容。

 

好比這樣:

root@docker-host-03:~/test_shell#more module/test1.sh

#!/bin/bash

 

#這裏我註釋掉的內容就是。並且這個變量名稱,儘可能按照某種規則確保是惟一的變量名稱。

#if [${m_log} ];then

#   return 0

#fi

#m_log="m_log"

#這一行輸出是爲了測試是否屢次導入了。

echo"TEST IS"

MODULENAME=$(basename$0)

LOGFILE=/tmp/logfile-`date+%Y%m%d`

 

log_info()

{

  #[ 2017-03-31 12:00:00 ] - TextName - The logmessage

  datetime=`date "+%Y-%m-%d%H:%M:%S"`

  if [ "$1" ];then

     echo "[ ${datetime} ] - ${MODULENAME}- $1 " | tee -a ${LOGFILE}

  else

    return 1

  fi

}

 


 這裏咱們在註釋掉的狀況下,看看屢次導入是什麼反應。

root@docker-host-03:~/test_shell# more  main.sh

#!/bin/bash

 

MODUPATH=$(dirname $(readlink -f$0))/module

echo ${MODUPATH}

export PATH=$PATH:${MODUPATH}

 

. test1.sh

. Config.sh

main()

{

 log_info 'This is the main shell'

 readConf

 echo${conf}

}

main

root@docker-host-03:~/test_shell# moremodule/Config.sh

#!/bin/bash

 

. test1.sh

readConf()

{

 conf="Read the config file"

}

root@docker-host-03:~/test_shell# ./main.sh

/root/test_shell/module

TEST IS

TEST IS

[ 2017-03-31 16:51:09 ] - main.sh - This isthe main shell

Read the config file

#咱們能夠看到這裏屢次輸出了TEST IS內容。


 

#將咱們上面註釋掉的內容去除,如今雖屢次導入,但實質上只會導入一次。這樣也會避免屢次循環引用。

root@docker-host-03:~/test_shell# ./main.sh

/root/test_shell/module

TEST IS

[ 2017-03-31 16:56:31 ] - main.sh - This isthe main shell

Read the config file

#當咱們去掉了註釋部分,這裏就不會出現屢次重複載入了。


-----------------------------小結-----------------

       其實這裏的模塊複用,其實就是經過source 或則. 的方式將本來的模塊內容「拷貝過來」,只是在使用的過程中,須要注意模塊路徑的問題,還有層級,和屢次調用的問題。

      至於功能複雜的shell,我想基本也不會複雜到沒法接受,基本按照必定的組織發方式就能變得清晰。而且提升複用。

       你們能夠本身積累一些比較經常使用的函數,須要用到的時候,導入,不用重複編寫。好比日誌,郵件告警等等。

----------------------------------------------------

相關文章
相關標籤/搜索