好湿?好紧?好多水好爽自慰,久久久噜久噜久久综合,成人做爰A片免费看黄冈,机机对机机30分钟无遮挡

主頁 > 知識庫 > shell腳本學習指南[三](Arnold Robbins & Nelson H.F. Beebe著)

shell腳本學習指南[三](Arnold Robbins & Nelson H.F. Beebe著)

熱門標簽:江蘇云電銷機器人公司 華鋒e路航港口地圖標注 打電話機器人接我是他的秘書 百度地圖標注錯了有責任嗎 地圖標注員都是年輕人 河南信譽好的不封卡電話外呼系統 客服外呼系統怎么樣 如果做線上地圖標注 揭陽智能電話機器人推薦

今天木有冷笑話,只有一個噩耗。噩耗是:今天木有冷笑話!??!
不要總想著冷笑話嘛,有點追求,聽毛主席的話:好好學習,天天向上!

第七章輸入輸出、文件與命令執行

學C的應該了解標準輸入輸出和錯誤輸出吧?感覺總打很多字進度太慢,所以一直在省略類似C的東西,也方便以后看這篇文章的人能夠快速學完shell腳本(或者是快速看完這本書)。

讀取行read命令是重要方式之一,它可以自標準輸入讀取行后,通過shell字段切割的功能(使用$IFS)進行切分,第一部分給第一個變量,第二部分給第二個,類推。如果切割單詞多余變量,則剩下所有的給最后一個變量。如果輸入行以反斜杠結尾,則read會丟棄反斜杠與換行符繼續讀取下行數據。它有一個選項 -r,它將忽視最后反斜杠當讀入數據。使用read可能的一個錯誤是通過循環讓read讀取一個文件如:
while IFS=: read user pass uid gid fullname homedir shell /etx/passwd ... 這個循環將一直下去并且每次read只讀passwd的第一行。因為每次循環都重新打開了passwd文件讀取內容。解決辦法是: cat /etc/passwd | while IFS=: read ....... 這樣通過管道解決讀取文件問題。 這里有一個概念,文件描述符,一般這個文件描述符是由0-9這幾個數字來描述的,所以傳統上shell也就允許你最多打開十個文件。比如make 1> results 2> ERRS 。命令make的標準輸出(文件描述符1)傳給results,并將錯誤輸出(文件描述符2)傳給ERRS。設置完文件描述符后,如何引用呢?像這樣:make > results 2>1 。
1> results這里的1其實沒必要,供輸出重定向的默認文件描述符是標準輸出,也就是文件描述符1,重定向 > results讓文件描述符1作為文件results,接下來重定向2>1有兩部分,2>重定向文件描述2,也就是標準錯誤輸出。而1就是剛才我們的疑問,用來引用我們定義的文件描述符。特別注意:2>1這樣的四個字符一定要連著寫。

再介紹一個用來改變shell本身I/O設置的exec命令。如果只有I/O重定向而沒有任何參數時,exec會改變shell的文件描述符:

復制代碼 代碼如下:

exec 2> /tmp/$0.log #重定向shell本身的標準錯誤輸出
exec 3 /some/file #打開新文件描述符3
...
read name rank serno 3 #從該文件讀取 exec 3>- #關閉文件描述符3

上例展示了如何關閉文件描述符。
exec還有一個功能就是在當前shell下執行指定的程序。

書中對printf做了完整的介紹,這里就不再介紹了,就是C里邊的那些東西。

shell中有兩種與文件名相關的展開:第一個是波浪號展開(~ tilde expansion),另一個叫法較多如通配符展開式(wildcard expansion)、全局展開(globbing)或路徑展開(pathname expansion)。

如果命令行字符第一個字符為波浪號或者變量指定的值里任何未被引號括起來的冒號之后的第一個字符為波浪號時,shell便會執行波浪號展開。波浪號展開的目的,是要將用戶根目錄的符號型表示方式,改為實際的目錄路徑。

shell環境下的通配符展開,有幾個基本的通配符:? , * , [set] [!set] ,前倆略過,第三個是匹配出現中括號集合中的字符,第四個取反義。比如可以查找 *.html 就知道處所有類似的文件。另外有一點注意的,在linux下文件名里的.號沒有任何特殊意義,匹配所有文件時只需用一個*即可,不用像windows下那樣*.*。
習慣上,當執行通配符展開時,shell會忽略文件名開頭為一個點號的文件。像這樣的點號文件(dot files)通常用作程序配置文件或啟動文件。

命令替換,書上寫的概念很繞口,其實就是一個命令的用法或者寫法,例如:
echo outer `echo inner1 \`echo inner2 \` `
這樣輸出結果就是 outer inner1 inner2 類似命令嵌套,從最內層開始執行。注意是反單引號,鍵盤1左邊與波浪號同鍵那個。但是這樣嵌套多了之后會非常難以閱讀,就出現了新的語法:
$ echo outer $(echo inner1 $(echo inner2) inner1) outer
這樣輸出結果就是outer inner1 inner2 inner1 outer。這樣清晰多了。

書中教了一個expr命令,從提到這個命令,到接下來的兩段都在說這個命令不好用,并且可以是由$(( )) ,test替代。但是可以了解一下,作用就是計算之后跟著的一個表達式比如:expr 1 + 1 。這里注意加號兩邊的空格,是必要的,書里貌似沒說,郁悶半天才發現必須添加空格。。- -!

這里又提了引用,就是說用來防止shell將某些東西解釋成你不想要的意義,比如你就是有就想要*,而不是需要一個通配符,這時候你需要轉義(\) ,或者是單引號引起來(單引號引起來的內容轉義符號也無效),或是雙引號?;煊玫臅r候請小心。

書中詳細說了一下命令的執行順與,感覺很有必要細看一下,全都摘錄一下。
shell從標準輸入或腳本中讀取的每一行成為管道;它包含了一個或多個命令,這些命令被零或多個管道字符隔開。事實上還有很多特殊符號可用來分隔單個的命令:分號;、管道|、、邏輯AND、還有邏輯OR||。對于每一個讀取的管道,shell都會將命令分割,為管道設置I/O,并且對每一個命令依次執行下面操作:
1、將命令分割成token,是以固定的一組meta字符分隔,有空格、制表符、換行字符、;、(、)、、>、|、和。token的種類包括單詞、關鍵字、輸出入重定向器,以及分號。這是微妙的,但是變量、命令還有算符替換,都可以在shell執行token認定的時候被執行。這就是為什么先前所舉例子vi ~$user/.profile中波浪符號可以展開像預期的那樣工作。
2、檢查每個命令的第一個token,看看是否它是不帶有引號或反斜杠的關鍵字。如果它是一個開放的關鍵字(if 或者 { (之類的),則這個命令其實是一個復合命令。shell為復合命令進行內部的設置,讀取下一條命令,并再次啟動進程。如果關鍵字非復合命令的開始符號(例如,它是控制結構的中間部分,像then、else或do,或是結尾部分,例如fi,done或邏輯運算符),則shell會發出語法錯誤的信號。
3、將每個命令的第一個單詞與別名列表對照檢查。如果匹配,它便代替別名的定義,并回到步驟1;否則,進行步驟4(別名是給交互式shell使用)?;氐讲襟E1,允許讓關鍵字的別名被定義:例如alias aslongas=while or alias procedure=function。注意,shell不會執行遞歸的別名展開,反而當別名展開為相同的命令時它會知道,并停止潛在的遞歸操作??梢酝ㄟ^引用要保護的單詞的任何部分而禁止別名展開。
4、如果波浪號字符出現在單詞的開頭處,則將 波浪號替換成用戶的跟目錄$HOME,將~user替換成user的根目錄。
波浪號替換會發生在下面的位置:
* 在命令行里,作為單詞的第一個未引用字符
* 在變量賦值中的=之后以及變量賦值中的任何:之后
* 形式${ varibale op word } 的變量替換里的word部分
5、將任何開頭為$符號的表達式,執行參數(變量)替換。
6、將任何形式為$(string)或 `string`的表達式,執行命令替換。
7、執行形式$((string))的算術表達式。
8、從參數、命令與算術替換中取出結果行的部分,再一次將它們切分為單詞。這次它使用$IFS里的字符作為定界符,而不是使用步驟1的那組meta字符。通常,在IFS里連續多個重復的輸入字符是作為單一定界符,這是你所期待的。這只有對空白字符而言是真的。對非空白字符,則不是這樣的。舉例說,當讀取以冒號分隔字段的/etc/passwd文件時,兩個連續冒號所界定的是一個空子段。
9、對于*、?以及一對[...]的任何出現次數,都執行文件名生成的操作,也就是通配符展開。
10、使用第一個單詞作為一個命令,遵循查找次序,也就是,先作為個特殊的內建命令,接著是作為函數,然后作為一般的內建命令,以及最后作為查找$PATH找到的第一個文件。
11、在完成I/O重定向與其他同類型事項之后,執行命令。

shell程序碰到一句命令,都會執行上邊的一次流程。比如:

復制代碼 代碼如下:

$ mkdir /tmp/x
$ cd /tmp/x
$ touch f1 f2
$ f=f y="a b"
$ echo ~+/${f}[12] $y $(echo cmd subst) $((3+2)) > out

命令一開始會根據shell語法分割token,最重要一點是I/O重定向 > out 在這里是被識別的,并存儲供稍后使用。最后這句echo被分為5個token,分別是: echo ,~+/${f}[12] , $y , $(echo cmd subst) ,$((3 + 2))這5個部分。
然后檢查第一個單詞echo是否為關鍵字,例如if或for,這里不是所以命令不變繼續處理。
檢查第一個單詞依然是echo 是否為別名,這里不是,繼續執行。
掃描所有單詞是否需要波浪號展開,本例中,~+為ksh93與bash的擴展,等同于$PWD,也就是當前的目錄。token 2將被修改變成如下:
echo /tmp/x/${f}[12] $y $(echo cmd subst) $((3 + 2))
下一步變量展開:token2與token3被修改變成:
echo /tmp/x/f[12] a b $(echo cmd subst) $((3 + 2))
再來要處理的是命令替換。注意,這里可遞歸引用列表里的所有步驟!在次例中,因為我們試圖讓所有的東西容易理解,因此命令替換修改了token4,結果如下:
echo /tmp/x/f[12] a b cmd subst $((3 + 2))
現在執行算術替換,結果如下:
echo /tmp/x/f[12] a b cmd subst 5
前面所有的展開產生的結果,都將再一次被掃描,看看是否有#IFS字符,如果有則它們是作為分隔符,產生額外的單詞。
最后的替換階段是通配符展開變化如下:
echo /tmp/x/f1 /tmp/x/f2 a b cmd subst 5
這時,shell已經準備好要執行最后的命令了。它會去尋找echo。正好ksh93與bash里的echo都已內建到shell中。
shell實際執行命令。首先執行>out的重定向,再調用內部的echo版本。這一系列完成了這句語句的執行。

eval語句是再告知shell取出eval的參數,并再執行它們一次,是他們經過整個命令行的處理步驟??匆粋€例子:

復制代碼 代碼如下:

listpage="ls | more"
$listpage

運行之后你會發現shell把|與more看作ls的參數,而不是直接產生一頁頁的文件列表。這是由于在shell執行變量時,管道字符出現在步驟5,也就是在它確實尋找管道字符之后(在步驟1).變量的展開一直要到步驟8才進行解析。結果,shell把 | 與more看作ls的參數,使得ls會試圖在當前目錄下尋找名為|與more的文件。

現在,想想eval $listpage吧,在shell到達最后一步時,會執行帶有ls、|與more參數的eval,這會讓shell回到步驟1,具有一行包括了這些參數的命令。在步驟1發現|后,將該行分割為兩個命令:ls 和more。每個要被處理的命令都以一般方式執行,最后的結果是在當前目錄下分頁的文件列表。

還有兩個其他的結構,有時也很有用,subShell與代碼塊。
subShell是一群被括在圓括號里的命令,這些命令會在另外的進程中執行。當你需要讓一小組的命令在不同的目錄下執行時,這些命令會在另外的進程中執行。如:
tar -cf - . | (cd /newdir; tar -xpf - )
左邊tar產生當前目錄打包文件,將它傳送給標準輸出。右邊的cd命令會先切換到新目錄,也就是讓打包文件在此目錄下解開。然后,右邊的tar將從打包文件里解開文件,請注意,執行此管道的shell并未更改它的目錄。

代碼塊概念上與subShell雷同,只不過它不會建立新進程。代碼塊用花括號括起。且會對主腳本造成影響(比如當前目錄)。一般花括號被視為關鍵字,即它們只有出現在命令的第一個符號時被識別。實際上,這表示你必須將結束花括號放置在換行符或分號之后。

shell有很多命令,之前說過,特殊內建命令與一般內建命令的差別在于shell查找要執行的命令時,會先查找特殊內建命令再找shell函數,接下來才是一般內建命令。最后是$PATH路徑內的外部命令。這種查找順序讓定義shell函數以擴展或覆蓋一般shell內建命令成為可能。舉例說你希望shell的提示號能包含當前目錄路徑的最后一個組成部分。最簡單的實現方式,就是在每次改變目錄時,都讓shell改變PS1.你可以寫一個自己專用的函數如下:

復制代碼 代碼如下:

# chdir ---改變目錄時更新PS1的個人函數
chdir () {
cd "$@" #實際更改目錄
x=$(pwd) #取得當前目錄的名稱
PS1="${x##*/}\$ " #截斷前面的組成部分后,指定給PS1
}

這么做有個問題,你必須在shell下輸入chdir而不是cd,這樣你可以自己寫一個名為cd的函數,然后shell會先找到你的cd函數,而不是一般內建函數cd。但是這樣又會有問題,shell函數如何真正訪問cd命令,這里函數內cd會再此調用你寫的cd函數導致遞歸出現。這時候我們需要轉義策略,使用內建命令command來告訴shell要避開函數的查找直接訪問真正的命令。

#cd --改變目錄時更新PS1的私人版
cd(){
command cd "$@"
x=$(pwd)
PS1="${x##*/}\$ "
}

第八章產生腳本


詳細講了一下set命令。。。

這一章詳解了兩個好用腳本的實現過程,這兩個腳本詳解內容揉雜在注釋里給出。

腳本一:功能是在給出的路徑下查找目標路徑

復制代碼 代碼如下:

#! /bin/sh -
#
# 標準輸出所產生的結果,通常是查找路徑下找到的每一個文件之第一個實體的完成路徑,
# 或是“filename: not found ”的標準錯誤輸出。
#
# 如果所有文件都找到,則退出碼為0,
# 否則,即為找不到的文件個數(非0)
# shell的退出碼限制為125

# 語法:
#      pathfind [--all] [--?] [--help] [--version] envvar pattern(s)
#
# 使用--all選項時,在路徑下的每一個目錄都會被查找,
# 而非停在第一個找到者。
# 所有腳本的頭部說明腳本功能是必不可少的,對人閱讀很有用。

#在網絡的環境下,安全性一直是必須慎重考慮的問題。其中有一種攻擊shell腳本  #的方式,是利用輸入字段分隔字符:IFS,它會影響shell接下來對輸入數據解釋的
#方式。為避免此類的攻擊,部分shell僅在腳本執行前,將IFS重設為標準值;其他
#則導入該變量的一個外部設置。很難在屏幕上看出來,單引號內包含一個換行一
#個空格和一個制表符,這是IFS的默認值。也可以使用轉義\040\t\n,但bourne
#shell不支持這一的轉義。重新定義IFS時有一點要特別留意,當"$*"展開以回復命
#令行時,IFS值的第一個字符,會被當成字段分隔符。這里不使用$*,不受影響
IFS='
  '
#另一種常見的安全性攻擊,則是欺騙軟件,它執行非我們所預期的命令。為了阻斷
#這種攻擊,我們希望調用的程序是可信任的版本,而非潛伏在用戶提供的查找路徑
#下的欺騙程序,因此我們將PATH設最小值,存儲初始值供以后使用。exprot語句
#是這里的關鍵么,它可以確保所有子進程繼承我們的安全查找路徑。
OLDPATH="$PATH"

PATH=/bin:/usr/bin
export PATH
#錯誤輸出函數
error(){
 echo "$@" 1>2
 usage_and_exit 1
}
#簡短的一個信息提示函數,$PROGRAM稍后會賦值為命令名
usage(){
 echo "Usage: $PROGRAM [--all] [--?] [--help] [--version] envvar pattern(s)"
}
#提供信息和狀態碼退出
usage_and_exit(){
 usage
 exit $1
}
#提供用戶版本號
version(){
 echo "$PROGRAM version $VERSION"
}
#給出警告信息并在狀態碼上加1,記錄警告次數
warning(){
 echo "$@" 1>2
 EXITCODE=`expr $EXITCODE + 1`
}
#按大寫全局,小寫局部命名規則,初始化需要用的變量
all=no
envvar=
EXITCODE=0
#basename會截去參數最后一個斜杠之前字串,返回剩下的部分
PROGRAM=`basename $0`
VERSION=1.0
#接下來這塊就是linux經典的命令行參數解析部分了,不多解釋,需要注意的是?號
#是通配符,所以篩選選項的時候防止展開加上單引號。
while test $# -gt 0
do
 case $1 in
 --all | --al | --a | -all | -al | -a )
  all=yes

 --help | --hel | --he | --h | '--?' | -help | -hel | -he | -h | '-?' )
  usage_and_exit 0

 --version | --versio | --versi | --vers | --ver | --ve | --v | \
 -version | -versio | -versi | -vers | -ver | -ve | -v )
  version
  exit 0

 -* )
  error "Unrecognized option: $1"

 * )
  break

#這里我小糾結了一下esac命令,沒搞清楚在這干嘛的,也沒這個命令說明,仔細看
#看才發現是case逆序寫法,是case的結束標志,就像if結束標志fi一樣
 esac
 shift
done
#下面我們要處理除選項外的參數了,我們可以用"$@"來取得,但是避免將他們存
#儲在變量內如files="$@",因為文件名中如果有空格將無法正確被處理。
envvar="$1"
test $# -gt 0 shift
#因為有可能用戶提供的環境變量是PATH,為安全性考慮會重設,這是我們檢測該變
#量,并適當更新envvar,開頭的x是為了避免開展當成test的選項。
test "x$envvar" = "xPATH" envvar=OLDPATH

#下邊這句雖然很段,但是最棘手部分:使用shell的eval語句。我們envvar里已經擁
#有了環境變量的名稱,可以"$envvar"取得,但我們現在要的是它的展開,我們也
#想要冒號分隔符轉換成一般空白分隔符。如果MYPATH為用戶所提供的名稱,我們
#便會構建參數字符串'${'"$envvar"'}',也是shell展開為'${MYPATH}'的等同物。兩邊
#的單引號是為了避免它更進一步展開,該字串傳給eval,它會將其視為兩個參數:
#echo與${MYPATH}。eval在環境下尋找MYPATH,假設找到就執行展開命令,并輸
#出,通過管道傳給tr命令將冒號轉換為空格,最后將轉化值給dirpath,錯誤信心隱藏
#輸給/dev/null
dirpath=`eval echo '${'"$envvar"'}' 2>/dev/null | tr : ' ' `

#為錯誤情況進行健全檢測
if test -z "$envvar"
then
 error Environment variable missing or empty
elif test "x$dirpath" = "x$envvar"
then
 error "Broken sh on this platform: cannot expand $envvar"
elif test -z "$dirpath"
then
 error Empty directory search path
elif test $# -eq 0
then
 exit 0
fi

#接下來三重循環,外層處理參數文件或模式,中層循環處理查找路徑下的目錄,內
#層循環匹配單一目錄下的文件。
for pattern in "$@"
do
 result=
 for dir in $dirpath
 do
  for file in $dir/$pattern
  do
   if test -f "$file"
   then
    result="$file"
    echo $result
    test "$all" = "no" break 2
   fi
  done
 done
 test -z "$result" warning "$pattern: not found"
done

#限制退出狀態是一般linux實現上的限制
test $EXITCODE -gt 125 EXITCODE=125
exit $EXITCODE

這里作者給留了課后作業:增添一個功能,不單單只能匹配文件,也能匹配其他東西比如:符號性連接文件,可讀取文件或者可執行文件之類的,需要test -x選項來進行匹配,本人完成的如下:

復制代碼 代碼如下:

#變量初始化的地方增添一個test選項變量,默認為f
testopt=f

#選項解析的位置增添test選項并檢測合法性
        --test | --tes | --te | --t | -test | -tes | -te | -t )
                echo $2
                if echo $2 | grep -e "^[bcdefgGhkLOPrSstuwx]\{1\}$"
                then
                        testopt=$2
                        shift
                else
                        error "Unrecognized --test option: $2"
                fi

 
#最后循環test匹配位置改為:
  if test -"$testopt" "$file"

歐了,課后作業完成,安全性還沒經驗,有老師批改作業木有?

下邊給了第二個腳本,是軟件構建自動化,代碼灰長長。。。這個打代碼都碼的頭疼了,跳過吧,有興趣童鞋自己搞定。我是不求甚解的先趕進度了,回來再搞。

復制代碼 代碼如下:

#! /bin/sh -
# 在一臺或多臺構建主機上,并行構建一個或多個包
#
# 語法:
# build-all [ --? ]
#    [ --all "..." ]
#    [ --check "..." ]
#    [ --configure "..." ]
#    [ --environment "..." ]
#    [ --help ]
#    [ --logdirectory dir ]
#    [ --on "[user@]host[:dir][,envfile] ..." ]
#    [ --source "dir..." ]
#    [ --userhosts "file(s)" ]
#    [ --version ]
#    package(s)
#
# 可選用的初始化文件:
# $HOME/.build/directories list of source directories
# $HOME/.build/userhosts  list of [user@]host[:dir][,envfile]

IFS='
  '

PATH=/usr/local/bin:/bin:/usr/bin
export PATH

UMASK=002
umask $UMASK

build_one(){
#語法:
# build_one [user@]host[:build-directory][,envfile]
 arg="`eval echo $1`"

 userhost="`echo $arg | sed -e 's/:.*$//'`"

 user="`echo $userhost | sed -e s'/@.*$//'`"
 test "$user" = "$userhost" user=$USER

 host="`echo $userhost | sed -e s'/^[^@]@//'`"

 envfile="`echo $arg | sed -e 's/^[^,]*,//'`"
 test "$envfile" = "$arg" envfile=/dev/null

 builddir="`echo $arg | sed -e s'/^[^,]*,//'`"
 test "$builddir" = "$arg" builddir=/tmp

 parbase=`basename $PARFILE`
 #NB:如果這些模式被更換過,則更新find_package()
 package="`echo $parbase | \
  sed -e 's/[.]jar$//' \
           -e 's/[.]tar[.]bz2$//' \
           -e 's/[.]tar[.]gz$//' \
           -e 's/[.]tar[.]Z$//' \
           -e 's/[.]tar$//' \
           -e 's/[.]taz$//' \
           -e 's/[.]zip$//'`"
 #如果我們在遠程主機上看不到包文件,則復制過去
 echo $SSH $SSHFLAGS $userhost "test -f $PARFILE"
 if $SSH $SSHFLAGS $userhost "test -f $PARFILE"
 then
  parbaselocal=$PARFLE
 else
  parbaselocal=$parbase
  echo $SCP $PARFILE $userhost:$builddir
  $SCP $PARFILE $userhost:$builddir
 fi
 #在遠程主機上解開存檔文件、構建,以及后臺執行方式檢查它
 sleep 1  #為了保證唯一的日志文件名
 now="`date $DATEFLAGS`"
 logfile="$package.$host.$now.log"
 nice $SSH $SSHFLAGS $userhost "
  echo '==================================================' ;
  test -f $BUILDBEGIN . $BUILDBEGIN || \
   test -f $BUILDBEGIN source $BUILDBEGIN || \
    true ;
  echo 'Package:   $package' ;
  echo 'Archive:   $PARFILE' ;
  echo 'Date:   $now' ;
  echo 'Local user:  $USER' ;
  echo 'Local host:  `hostname`' ;
  echo 'Local log directory: $LOGDIR' ;
  echo 'Local log file:  $logfile' ;
  echo 'Remote user:  $user' ;
  echo 'Remote host:  $host' ;
  echo 'Remote directory:  $builddir' ;
  printf 'Remote date:  ' ;
  date $DATEFLAGS ;
  printf 'Remote uname:  ' ;
  uname -a || true ;
  printf 'Remote gcc version: ' ;
  gcc --version | head -n 1 || echo ;
  printf 'Remote g++ version: ' ;
  g++ --version | head -n 1 || echo ;
  echo 'Configure environment: `$STRIPCOMMENTS $envfile | \
   $JOINLINES`' ;
  echo 'Extra environment:  $EXTRAENVIRONMENT' ;
  echo 'Configure directory: $CONFIGUREDIR' ;
  echo 'Configure flags:  $CONFIGUREFLAGS' ;
  echo 'Make all targets:  $ALLTARGETS' ;
  echo 'Make check targets: $CHECKTARGETS' ;
  echo 'Disk free report for $builddir/$package:' ;
  df $builddir | $INDENT ;
  echo 'Environment:' ;
  env | env LC_ALL=C sort | $INDENT ;
  echo '==============================================' ;
  umask $UMASK ;
  cd $builddir || exit 1 ;
  /bin/rm -rf $builddir/$package ;
  $PAR $parbaselocal ;
  test "$parbase" = "$parbaselocal" /bin/rm -f $parbase ;
  cd $package/$CONFIGUREDIR || exit 1 ;
  test -f configure \
   chmod a+x configure \
    env `$STRIPCOMMENTS $envfile | $JOINLINES` \
     $EXTRAENVIRONMENT \
     nice time ./configure $CONFIGUREFLAGS ;
  nice time make $ALLTARGETS nice time make $CHECKTARGETS ;
  echo '===============================================' ;
  echo 'Disk free report for $builddir/$package:' ;
  df $builddir | $INDENT ;
  printf 'Remote date: ' ;
  date $DATEFLAGS ;
  cd ;
  test -f $BUILDEND . $BUILDEND || \
   test -f $BUILDEND source $BUILDEND || \
    true;
  echo '===============================================' ;
 " /dev/null > "$LOGDIR/$logfile" 2>1
}

error(){
 echo "$@" 1>2
 usage_and_exit 1
}

find_file(){
#語法:
# find_file file program-and-args
#如果找到,返回0,如果找不到返回1
 if test -r "$1"
 then
  PAR="$2"
  PARFILE="$1"
  return 0
 else
  return 1
 fi
}

find_package(){
#語法:
# find_package package-x.y.z
 base=`echo "$1" | sed -e 's/[-_][.]*[0-9].*$//'`
 PAR=
 PARFILE=
 for srcdir in $SRCDIRS
 do
  test "$srcdir" = "." srcdir="`pwd`"
  for subdir in "$base" ""
  do
  #如果此列表有改變,則更新build_one()內的包設置
  find_file $srcdir/$subdir/$1.tar.gz "tar xfz" return
  find_file $srcdir/$subdir/$1.tar.Z "tar xfz" return
  find_file $srcdir/$subdir/$1.tar "tar xf" return
  find_file $srcdir/$subdir/$1.tar.bz2 "tar xfj" return
  find_file $srcdir/$subdir/$1.tar.tgz "tar xfz" return
  find_file $srcdir/$subdir/$1.tar.zip "unzip -q" return
  find_file $srcdir/$subdir/$1.jar "jar xf" return
  done
 done
}

set_userhosts(){
#語法:
# set_userhosts file(s)
 for u in "$@"
 do
  if test -r "$u"
  then
   ALTUSERHOSTS="$ALTUSERHOSTS $u"
  elif test -r "$BUILDHOME/$u"
  then
   ALTUSERHOSTS="$ALTUSERHOSTS $BUILDHOME/$u"
  else
   error "File not found: $u"
  fi
 done
}

usage(){
cat EOF
 Usage:
  $PROGRAM [ --? ]
    [ --all "..." ]
    [ --check "..." ]
    [ --configure "..." ]
    [ --environment "..." ]
    [ --help ]
    [ --logdirectory dir ]
    [ --on "[user@]host[:dir][,envfile] ..." ]
    [ --source "dir ..." ]
    [ --userhosts "file(s)" ]
    [ --version ]
    package(s)
EOF
}

usage_and_exit(){
 usage
 exit $1
}

version(){
 echo "$PROGRAM version $VERSION"
}

warning(){
 echo "$@" 1>2
 EXITCODE=`expr $EXITCODE + 1 `
}

ALLTARGETS=
altlogdir=
altsrcdirs=
ALTUSERHOSTS=
BUILDBEGIN=./.build/begin
BUILDEND=./.build/end
BUILDHOME=$HOME/.build
CHECKTARGETS=check
CONFIGUREDIR=.
CONFIGUREFLAGS=
DATEFLAGS="+%Y.%m.%d.%H.%M.%S"
EXITCODE=0
EXTRAENVIRONMENT=
INDENT="awk '{ print \"\t\t\t\" \$0 }'"
JOINLINES="tr '\n' '\040'"
LOGDIR=
PROGRAM=`basename $0`
SCP=scp
SSH=ssh
SSHFLAGS=${SSHFLAGS--x}
STRIPCOMMENTS='sed -e s/#.*$//'
userhosts=
VERSION=1.0

#默認的初始化文件
defaultdirectories=$BUILDHOME/directories
defaultuserhosts=$BUILDHOME/userhosts

#要尋找包分發的位置列表,如果用戶未提供個人化列表,則使用默認列表:
SRCDIRS="`$STRIPCOMMENTS $defaultdirectories 2> /dev/null`"
test -z "$SRCDIRS" \
 SRCDIRS=".
  /usr/local/src
  /usr/local/gnu/src
  $HOME/src
  $HOME/gnu/src
  /tmp
  /usr/tmp
  /var/tmp"
while test $# -gt 0
do
 case $1 in
 --all | --al | --a | -all | -al | -a )
  shift
  ALLTARGETS="$1"

 --cd | -cd )
  shift
  CONFIGUREDIR="$1"

 --check | --chec | --che | --ch | -check | -chec | -che | -ch )
  shift
  CHECKTARGETS="$1"

 --configure | --conf | --co | -configure | -conf | -co )
  shift
  CONFIGUREFLAGS="$1"

 --environment | --environ | -- envir | --e | -environment | \
  -environ | -envir | -e )
  shift
  EXTRAENVIRONMENT="$1"

 --help | --h | '--?' | -help | -h | '-?' )
  usage_and_exit 0

 --logdirectory | --log | --l | -logdirectory | -log | -l )
  shift
  altlogdir="$1"

 --on | --o | -on | -o )
  shift
  userhosts="$userhosts $1"

 --source | --s | -source | -s )
  shift
  altsrcdirs="$altsrcdirs $1"

 --userhosts | --u | -userhosts | -u )
  shift
  set_userhosts $1

 --version | --v | -version | -v )
  version
  exit 0

 -* )
  error "Unrecognized option: $1"

 * )
  break

 esac
 shift
done

#尋找適當的郵件客戶端程序
for MAIL in /bin/mailx /usr/bin/mailx /usr/sbin/mailx /usr/ucb/mailx \
  /bin/mail /usr/bin/mail
do
 test -x $MAIL break
done
test -x $MAIL || error "Cannot find mail client"

#命令行來源目錄優先于默認值
SRCDIRS="$altsrcdirs $SRCDIRS"

if test -n "$userhosts"
then
 test -n "$ALTUSERHOSTS"
   userhosts="$userhosts `$STRIPCOMMENTS $ALTUSERHOSTS 2> /dev/null`"
else
 test -z "$ALTUSERHOSTS" ALTUSERHOSTS="$defaultuserhosts"
 userhosts="`$STRIPCOMMENTS $ALTUSERHOSTS 2> /dev/null`"
fi

#檢查是否要執行某些操作
test -z "$userhosts" usage_and_exit 1

for p in "$@"
do
 find_package "$p"

 if test -z "$PARFILE"
 then
  warning "Cannot find package file $p"
 fi

 LOGDIR="$altlogdir"
 if test -z "$LOGDIR" -o ! -d "$LOGDIR" -o ! -w "$LOGDIR"
 then
  for LOGDIR in "`dirname $PARFILE`/logs/$p" \
  $BUILDHOME/logs/$p /usr/tmp /var/tmp /tmp
  do
   test -d "$LOGDIR" || mkdir -p "LOGDIR" 2> /dev/null
   test -d "$LOGDIR" -a -w "$LOGDIR" break
  done
 fi

 msg="Check build logs for $p in `hostname`:$LOGDIR"
 echo "$msg"
 echo "$msg" | $MAIL -s "$msg" $USER 2> /dev/null

 for u in $userhosts
 do
  build_one $u
 done
done

#將退出狀態限制為一般unix實際做法
test $EXITCODE -gt 125 EXITCODE=125

exit $EXITCODE

個人原創,轉載請注明:三江小渡

您可能感興趣的文章:
  • shell腳本學習指南[二](Arnold Robbins & Nelson H.F. Beebe著)
  • shell腳本學習指南[一](Arnold Robbins & Nelson H.F. Beebe著)
  • shell腳本學習指南[六](Arnold Robbins & Nelson H.F. Beebe著)
  • shell腳本學習指南[五](Arnold Robbins & Nelson H.F. Beebe著)
  • shell腳本學習指南[四](Arnold Robbins & Nelson H.F. Beebe著)

標簽:婁底 巴彥淖爾 淘寶邀評 馬鞍山 金昌 邵陽 許昌 赤峰

巨人網絡通訊聲明:本文標題《shell腳本學習指南[三](Arnold Robbins & Nelson H.F. Beebe著)》,本文關鍵詞  shell,腳本,學習指南,三,;如發現本文內容存在版權問題,煩請提供相關信息告之我們,我們將及時溝通與處理。本站內容系統采集于網絡,涉及言論、版權與本站無關。
  • 相關文章
  • 下面列出與本文章《shell腳本學習指南[三](Arnold Robbins & Nelson H.F. Beebe著)》相關的同類信息!
  • 本頁收集關于shell腳本學習指南[三](Arnold Robbins & Nelson H.F. Beebe著)的相關信息資訊供網民參考!
  • 推薦文章
    主站蜘蛛池模板: 好想被cao啊随便cao求cao| 无国产精品白浆是免费| 精品国产福利第一区二区三区| 国产激情美女丝袜激情视频| 公交车上荫蒂添的好舒服视频 | 老婆老婆老婆东度日| 美人娇全文阅读| 91福利国产在线观看菠萝蜜| 青青青视频免费| 美女禁区视频免费观看精选| 欧美另类videosbestsex视频| 青柠影视在线播放免费动漫| 善良的么公和熄日本中文字幕| 国产精品一二区| 全黄做爰毛片免费看| 啊啊啊不要射进去| 黄色伦理电影| 第一次破学生处好爽| 肥婆老熟女一区二区三区四区| jizzz老师| 黑人狂躁日本妞| 忘穿内裤被同桌摸喷水| 我与futa女总裁的羞耻生活 | 一级女性全黄A片色情女狼| 囯产精品久久久久久久久久元玛| 美女裸体秘?无遮挡視頻图片| 在线欧美69v免费观看视频| 完整的强床戏视频| 《两男一女》未删减版| 丁香色欲久久久久久综合网| 娇小初叫摘花第一次vi| 男女边摸边吃奶边做爽免费视频 | 被原上司紧缚波多野结衣中字| 曰逼| 东京热无码人妻精品一区二区三区| 国产白丝学生浆喷水在线视频| 韩国漫画巜爆乳女教师3| 国产麻豆麻豆| 野爱| 一品道综合色爱久久久| 久久久中文|