Linux基础:Shell脚本学习
一、shell簡介
Shell是用戶和Unix/Linux內(nèi)核溝通的橋梁,用戶的大部分工作都是通過Shell完成的。Shell既是一種命令語言,又是一種程序設(shè)計語言。作為命令語言,它交互式地解釋和執(zhí)行用戶輸入的命令;作為程序設(shè)計語言,它定義了各種變量和參數(shù),并提供了許多在高級語言中才具有的控制結(jié)構(gòu),包括循環(huán)和分支。
? ? 它雖然不是Unix/Linux系統(tǒng)內(nèi)核的一部分,但它調(diào)用了系統(tǒng)核心的大部分功能來執(zhí)行程序、建立文件并以并行的方式協(xié)調(diào)各個程序的運行。因此,對于用戶來說,shell是最重要的實用程序,深入了解和熟練掌握shell的特性極其使用方法,是用好Unix/Linux系統(tǒng)的關(guān)鍵。
????Shell 腳本: 其實就是命令的堆積。
Shell有兩種執(zhí)行命令的方式:
交互式(Interactive):解釋執(zhí)行用戶的命令,用戶輸入一條命令,Shell就解釋執(zhí)行一條。
批處理(Batch):用戶事先寫一個Shell腳本(Script),其中有很多條命令,讓Shell一次把這些命令執(zhí)行完,而不必一條一條地敲命令。
? ? shell只定義了一個非常簡單的編程語言,所以,如果你的腳本程序復(fù)雜度較高,或者要操作的數(shù)據(jù)結(jié)構(gòu)比較復(fù)雜,那么還是應(yīng)該使用Python、Perl這樣的腳本語言,或者是你本來就已經(jīng)很擅長的高級語言。因為sh和bash在這方面很弱,比如說:
它的函數(shù)只能返回字串,無法返回數(shù)組
它不支持面向?qū)ο?#xff0c;你無法實現(xiàn)一些優(yōu)雅的設(shè)計模式
它是解釋型的,一邊解釋一邊執(zhí)行,連PHP那種預(yù)編譯都不是,如果你的腳本包含錯誤(例如調(diào)用了不存在的函數(shù)),只要沒執(zhí)行到這一行,就不會報錯
二、第一個shell腳本
? ? Shell腳本典型的開發(fā)周期:直接在命令行(command line)上測試。然后,一旦找到能夠完成工作的適當(dāng)語法,再將它們放進(jìn)一個獨立的腳本里,并為該腳本設(shè)置執(zhí)行的權(quán)限。
1、編寫
? ? 打開文本編輯器,新建一個文件,擴(kuò)展名最好為sh(sh代表shell),雖然擴(kuò)展名并不影響腳本執(zhí)行,但是便于我們識別,而且對于編輯器來說,還可能進(jìn)行語法高亮。
#!/bin/bash#?print?the?"hello?world" echo?"Hello?World"2、執(zhí)行
運行Shell腳本有兩種方法:
作為可執(zhí)行程序
注意:一定是 ./first.sh。為什么呢?通常我們在執(zhí)行命令的時候,shell會根據(jù)PATH變量定義的路徑進(jìn)行查找,而我們的當(dāng)前路徑一般不會加入PATH變量,所以就會提示?-bash: first.sh : command not found。
./first.sh ?是通過相對路徑,指明執(zhí)行 當(dāng)前目錄下的叫做 first.sh 的文件。
作為解釋器參數(shù)
?3、運行原理
? ? 當(dāng)shell執(zhí)行一個程序時,會要求UNIX內(nèi)核啟動一個新的進(jìn)程(process),以便在該進(jìn)程里執(zhí)行所指定的程序。內(nèi)核知道如何為“編譯型”程序做這件事。shell腳本并非編譯型程序;當(dāng)shell要求內(nèi)核執(zhí)行它時,內(nèi)核將無法做這件事,并回應(yīng)“not executable format file”錯誤信息。shell收到此錯誤信息時,就會認(rèn)為“這不是編譯型程序,那么一定是shell腳本”,”退回到shell”接著會啟動一個新的/bin/sh 副本來執(zhí)行該程序。
? ? 當(dāng)系統(tǒng)只有一個shell時,“退回到/bin/sh”的機(jī)制非常方便。但現(xiàn)行的系統(tǒng)都會擁有好幾個shell,因此需要通過一種方式,告知內(nèi)核應(yīng)該以哪個shell來執(zhí)行所指定的shell腳本。事實上,這么做有助于執(zhí)行機(jī)制的通用化,讓用戶得以直接引用任何的程序語言解釋器,而非只是一個命令shell。方法是,通過腳本文件特殊的第一行來設(shè)置:在第一行的開頭處使用 #!這兩個字符(必須:頂行&&頂頭)。
????在計算機(jī)科學(xué)中,sha-bang是一個由井號和嘆號構(gòu)成的字符串行(#!),其出現(xiàn)在文本文件的第一行的最前兩個字符。 在文件中存在sha-bang的情況下,類Unix操作系統(tǒng)的程序載入器會分析sha-bang后的內(nèi)容,將這些內(nèi)容作為解釋器指令,并調(diào)用該指令,并將載有sha-bang的文件路徑作為該解釋器的參數(shù)[1]。#! (magic number)為了讓Linux內(nèi)核識別這是什么格式的文件。
* * *
????The sha-bang ( #!) ?at the head of a script tells your system that this file is a set of commands to be fed to the command interpreter indicated. The #! is actually a two-byte magic number, a special marker that designates a file type, or in this case an executable shell script (type man magic for more details on this fascinating topic). Immediately following the sha-bang is a path name. This is the path to the program that interprets the commands in the script, whether it be a shell, a programming language, or a utility. This command interpreter then executes the commands in the script, starting at the top (the line following the sha-bang line), and ignoring comments.?
#!/bin/sh #!/bin/bash #!/usr/bin/perl #!/usr/bin/tcl #!/bin/sed?-f #!/bin/awk?-f????Each of the above script header lines calls a different command interpreter, be it /bin/sh, the default shell (bash in a Linux system) or otherwise. Using #!/bin/sh, the default Bourne shell in most commercial variants of UNIX, makes the script portable to non-Linux machines, though you sacrifice Bash-specific features. The script will, however, conform to the POSIX ?sh standard.
Note that the path given at the "sha-bang" must be correct, otherwise an error message -- usually "Command not found." -- will be the only result of running the script.?
#! can be omitted if the script consists only of a set of generic system commands, using no internal shell directives. The second example, above, requires the initial #!, since the variable assignment line, lines=50, uses a shell-specific construct. Note again that #!/bin/sh invokes the default shell interpreter, which defaults to /bin/bash on a Linux machine.
? This tutorial encourages a modular approach to constructing a script. Make note of and collect "boilerplate" code snippets that might be useful in future scripts. Eventually you will build quite an extensive library of nifty routines. As an example, the following script prolog tests whether the script has been invoked with the correct number of parameters.
When you execute a program, the kernel checks whether it starts by some magic byte sequence. If the executable file starts with #!, the kernel interprets the rest of the line as an interpreter name.?
????If the executable file starts with \177ELF (where \177 is byte 127), it loads the file as an ELF executable; that's the normal kind on most unix systems nowadays.
????If the kernel doesn't recognize the file format, it refuses to execute the file and returns the error ENOEXEC (Exec format error). When the shell notices that, it takes upon itself to execute the program as a shell script. If the magic line is not provided, a default shell is used to run the script. This default shell could either be Bourne shell (sh) which is the case in some flavors, however, in some other flavors, the default shell used is same as login shell to execute it. The thing is: Don't leave it to the system to decide the shell, always provide the shell which you want in the first line.
[root@skype?~]#?file?/etc/init.d/sshd /etc/init.d/sshd:?Bourne-Again?shell?script?text?executable [root@skype?~]#?file?/bin/ls /bin/ls:?ELF?64-bit?LSB?executable,?x86-64,?version?1?(SYSV),?dynamically?linked?(uses?shared?libs),?for?GNU/Linux?2.6.18,?strippedQ. Sha-Bang(#!)的編寫有什么規(guī)范?
A. Sha-Bang(#!)應(yīng)該位于腳本的第一行,并且頂格填寫,否則都是錯的,即使Sha-Bang之前的內(nèi)容都是注釋,這種錯誤是常見的,而且不易發(fā)現(xiàn)的,因為此時Sha-Bang(#!)所在行實際上是不起效的,系統(tǒng)使用了默認(rèn)的命令行解釋器
Q. 為什么推薦這種寫法:#!/bin/env perl?
A. 因為這是有利于移植腳本到其它平臺的寫法,解釋器的默認(rèn)安裝路徑在各個操作系統(tǒng)是不太一樣的,有的是/bin/,有的是/usr/bin/,甚至有可能是用戶自定義的路徑,使用env就基本上能夠通用了。雖然env也有可能在/bin/或者/usr/bin/中,但通常的情況是在這兩個路徑下都有env,或者其中一個是另一個的符號鏈接
????用戶可以指定命令解釋器,而非只是一個命令shell。通過腳本文件中特殊的第一行來設(shè)置:在第一行的開頭處使用 #! 這兩個字符。當(dāng)一個文件的開頭字符是 #! 時,內(nèi)核會掃描該行的其余部分,看是否存在可用來執(zhí)行程序的解釋器的完整路徑。(中間如果出現(xiàn)任何空白符號都會略過),此外,內(nèi)核還會掃描是否有一個選項(有且只能有一個)要傳遞給解釋器,內(nèi)核會以被指定的選項來引用解釋器。
#! /bin/csh –f ? ? ? ??
#! /bin/awk –f (直接通過內(nèi)核調(diào)用awk程序解釋后面的腳本,而不是shell間接調(diào)用)
假設(shè)有一個awk腳本,/usr/test.awk,它的第一行如下: #! /bin/awk –f,如果shell的查找路徑(PATH)中有 /usr,當(dāng)用戶鍵入 test.awk時,內(nèi)核解釋 #! 這行后,便會以如下的方式來引用awk:
? ? ? ? ?/bin/awk–f ?/usr/test.awk
這樣的機(jī)制讓我們得以輕松地引用任何的解釋器。
?
4、注釋
以”#“開頭的行都是注釋,會被解釋器忽略。
5、輸出
????echo命令將參數(shù)打印到標(biāo)準(zhǔn)輸出(把字符串轉(zhuǎn)換為數(shù)據(jù)流),參數(shù)之間以一個空格隔開,并以換行符號結(jié)尾。-n選項會省略結(jié)尾的換行符。` ` 或者 $()命令則把數(shù)據(jù)流轉(zhuǎn)換為字符串。
????printf 命令模仿C程序庫里的printf()庫函數(shù)。它幾乎復(fù)制了其所有功能。完整的語法分為兩部分: printf ?“format-string” ?[argumnets …]
參數(shù)以空白符分隔,如果參數(shù)的個數(shù)比格式聲明的多,則printf會循環(huán)且依次的使用格式字符串里的格式聲明,直到處理完參數(shù)。意味著把參數(shù)依次左移處理。這是和C printf函數(shù)最大的不同。
三、條件判斷
????很多時候,我們都需要進(jìn)行條件判斷,然后對不同的結(jié)果產(chǎn)生不同的行為。比如判斷 3 是否 大于 2,我們可能想當(dāng)然像下面這樣,在命令行上:
很遺憾,在命令行上,不能直接這樣測試,因為對于 shell 來說, > , < 都是元(meta)字符,具有特殊含義(重定向)。
????為了解決這種尷尬,于是shell專門提供了關(guān)于測試的命令: test, [ , ` `, 可以針對 數(shù)字、字符串、文件進(jìn)行測試。可以man bash得到更多的信息,在里面找到對”CONDITIONAL EXPRESSIONS”的描述。
test 與 [ ?同樣用于條件測試:?"["是一個可執(zhí)行程序,路徑是"/usr/bin/[", [ 是一個命令,它是內(nèi)置命令test的簡寫形式,只不過它要求最后一個參數(shù)必須是 ]。在使用[ ] 進(jìn)行判定的時候有一個事項要注意的是,在括號兩邊以及符號兩邊均要有空格。
運算符????????????描述??????????????示例 ##?文件狀態(tài)測試 -e?filename?????如果filename?存在,則為真???????[?-e?/var/log/syslog?] -d?filename?????如果filename?為目錄,則為真??????[?-d?/tmp/mydir?] -f?filename?????如果filename?為常規(guī)文件,則為真????[?-f?/usr/bin/grep?] -L?filename?????如果filename?為符號鏈接,則為真????[?-L?/usr/bin/grep?] -r?filename?????如果filename?可讀,則為真???????[?-r?/var/log/syslog?] -w?filename?????如果filename?可寫,則為真???????[?-w?/var/mytmp.txt?] -x?filename?????如果filename?可執(zhí)行,則為真??????[?-L?/usr/bin/grep?] filename1?-nt?filename2??如果?filename1?比?filename2?新,則為真?[?/tmp/install/etc/services-nt?/etc/services?] filename1?-otfilename2???如果filename1?比filename2?舊,則為真??[/boot/bzImage?-ot?arch/i386/boot/bzImage?]##?字符串測試?(請注意引號的使用,這是防止空格擾亂代碼的好方法) -z?string????????如果?string?長度為零,則為真?????[?-z?$myvar?] -n?string????????如果?string?長度非零,則為真?????[-n?$myvar?] string1?=?string2???如果?string1?與?string2?相同,則為真?[$myvar?=?one?two?three?] string1?!=?string2???如果?string1?與?string2?不同,則為真????[$myvar?!=?one?two?three?] string1?<?string2???????如果string1在本地的字典序列中排在string2之前,則為真?????[[$myvar?<?"one"?]] string1?>?string2???????如果string1在本地的字典序列中排在string2之后,則為真?????[[$myvar?>?"one"?]]##?算術(shù)測試 num1?-eq?num2?????????????等于???????[?3?-eq?$mynum?] num1?-ne?num2?????????????不等于??????[?3?-ne?$mynum?] num1?-lt?num2?????????????小于???????[?3?-lt?$mynum?] num1?-le?num2?????????????小于或等于????[?3?-le?$mynum?] num1?-gt?num2?????????????大于???????[?3?-gt?$mynum?] num1?-ge?num2?????????????大于或等于????[?3?-ge?$mynum?]bash?[?] 單雙括號
基本要素:
??[ ] 兩個符號左右都要有空格分隔
??內(nèi)部操作符與操作變量之間要有空格:如? [ ?“a” ?= ?“b” ?]
??字符串比較中,>?< 需要寫成\> \< 進(jìn)行轉(zhuǎn)義
??[ ] 中字符串或者${}變量盡量使用"" 雙引號擴(kuò)住,避免值未定義引用而出錯的好辦法
??[ ] 中可以使用 –a –o 進(jìn)行邏輯運算
??[ ] 是bash 內(nèi)置命令:[ is a shell builtin
bash? [[? ]] 雙方括號
基本要素:
??` ` 兩個符號左右都要有空格分隔
??內(nèi)部操作符與操作變量之間要有空格:如? [[ ?“a”?= ?“b” ?]]
??字符串比較中,可以直接使用 >?< 無需轉(zhuǎn)義
??` ` 中字符串或者${}變量盡量如未使用"" 雙引號擴(kuò)住的話,會進(jìn)行模式和元字符匹配
??[[] ] 內(nèi)部可以使用 && ?|| 進(jìn)行邏輯運算
??` ` 是bash ?keyword:[[ is a shell keyword
` `?其他用法都和[ ]?一樣
[[ ?]]?比[ ]?具備的優(yōu)勢
? ?①[[是 bash 程序語言的關(guān)鍵字。并不是一個命令,` ` 結(jié)構(gòu)比[ ]結(jié)構(gòu)更加通用。在[[和]]之間所有的字符都不會發(fā)生文件名擴(kuò)展或者單詞分割,但是會發(fā)生參數(shù)擴(kuò)展和命令替換。
??? ②支持字符串的模式匹配,使用=~操作符時甚至支持shell的正則表達(dá)式。字符串比較時可以把右邊的作為一個模式,而不僅僅是一個字符串,比如[[ hello == hell? ]],結(jié)果為真。` ` 中匹配字符串或通配符,不需要引號。 ?
??? ③使用` `.``.``.` `條件判斷結(jié)構(gòu),而不是[... ],能夠防止腳本中的許多邏輯錯誤。比如,&&、||、<和> 操作符能夠正常存在于` `條件判斷結(jié)構(gòu)中,但是如果出現(xiàn)在[ ]結(jié)構(gòu)中的話,會報錯。 ?
??? ④bash把雙中括號中的表達(dá)式看作一個單獨的元素,并返回一個退出狀態(tài)碼。
? ? ? ?使用` `.``.``.` `條件判斷結(jié)構(gòu), 而不是[ ... ], 能夠防止腳本中的許多邏輯錯誤. 比如,&&, ||, <, 和> 操作符能夠正常存在于[[]]條件判斷結(jié)構(gòu)中, 但是如果出現(xiàn)在[ ]結(jié)構(gòu)中的話, 會報錯。
注意事項:
因為Bash變量不是強(qiáng)類型的,所以你應(yīng)該小心:#!/bin/basha=4 b=5#??Here?"a"?and?"b"?can?be?treated?either?as?integers?or?strings. #??There?is?some?blurring?between?the?arithmetic?and?string?comparisons, #+?since?Bash?variables?are?not?strongly?typed.#??Bash?permits?integer?operations?and?comparisons?on?variables #+?whose?value?consists?of?all-integer?characters. #??Caution?advised,?however.echoif?[?"$a"?-ne?"$b"?] thenecho?"$a?is?not?equal?to?$b"echo?"(arithmetic?comparison)" fiechoif?[?"$a"?!=?"$b"?] thenecho?"$a?is?not?equal?to?$b."echo?"(string?comparison)"#?????"4"??!=?"5"#?ASCII?52?!=?ASCII?53 fi#?In?this?particular?instance,?both?"-ne"?and?"!="?work.echoexit?0????As S.C. points out, in a compound test, even quoting the string variable might not suffice.?[ -n "$string" -o "$a" = "$b" ]?may cause an error with some versions of Bash if?$string?is empty. The safe way is to append an extra character to possibly empty variables,?[ "x$string" != x -o "x$a" = "x$b" ]?(the?"x's"?cancel out).
????各位想過一個問題沒有,為什么能夠進(jìn)行測試呢?是根據(jù)什么來進(jìn)行判斷的呢?比如 if 語句:
????當(dāng)我們執(zhí)行命令后,通常會返回2類值:狀態(tài)返回碼(exit status),以及命令執(zhí)行返回結(jié)果:標(biāo)準(zhǔn)輸出或標(biāo)準(zhǔn)錯誤輸出。測試命令是根據(jù) ?exit status 進(jìn)行判斷的,和 標(biāo)準(zhǔn)輸出的信息沒有半毛錢關(guān)系。
寫了一個很2B的程序:
結(jié)果無論怎么執(zhí)行,都是執(zhí)行的 else 部分。單獨執(zhí)行每個命令,如下
修改如下即可:
?所以:Shell腳本典型的開發(fā)周期:直接在命令行(command line)上測試。然后,一旦找到能夠完成工作的適當(dāng)語法,再將它們放進(jìn)一個獨立的腳本里,并為該腳本設(shè)置執(zhí)行的權(quán)限。
四、算術(shù)運算
大家可以參考:http://mingxinglai.com/cn/2013/01/different-ways-of-doing-arithmetic-operators-in-linux/
6種算術(shù)運算方法是:
let operation
expr operation
$[ operation ]
$(( operation ))
用 awk 做算術(shù)運算(算術(shù)擴(kuò)展)
echo "operation" | bc
前面4種shell 腳本進(jìn)行算術(shù)運算的方法, $(()) 最為常用,可以像 C語言 一樣流暢的寫表達(dá)式,所有變量可以不加入:“$”符號前綴。(()) 進(jìn)行運算, $(()) 取出運算結(jié)果。有了它,我們就可以拋棄: let, expr 命令了。
a=$((a+1,?b++,?--c)); echo?$a,$b,$c但是它們都有一個致命的缺陷,都不支持浮點數(shù)。Bash僅支持整數(shù)運算(直接把小數(shù)部分截斷(Truncate))。
這里有一個奇怪的現(xiàn)象:
[root@skype?~]#?echo?$((10.3?/?3))?&>?/dev/null -bash:?10.3?/?3:?syntax?error:?invalid?arithmetic?operator?(error?token?is?".3?/?3")為什么無法進(jìn)行重定向呢? 因為重定向是 shell把其他進(jìn)程的輸入輸出進(jìn)行重定向。 而這個錯誤是 bash本身的致命錯誤,所以當(dāng)然無法重定向咯。
扯遠(yuǎn)了,那么此時我們可以通過 bc, awk 來進(jìn)行更復(fù)雜的運算或浮點運算。
bc 交互模式:
bc非交互式:
直接把算術(shù)表達(dá)式送給bc即可,如果要指定精度,加上scale=N; 用分號隔開
#?+,?-,?*,?/?,?^ [root@skype?~]#?echo?'scale?=?2;?9?+?8?*?2?-?6?/?5?+?2^3'?|?bc 31.80#?functions [root@skype?~]#?echo?'scale?=?2;?sqrt(15)'?|?bc 3.87#?進(jìn)制轉(zhuǎn)換 [root@skype?~]#?echo?'ibase=16;?obase?=?2;?8'?|?bc 1000五、流程控制
===== for?var?in?item1?item2?...?itemN docommand1command2...commandN done #?把?var分別賦值,?var=item1,?var=item2,?...var=itemN===== for?file?in?* #???????????^??Bash?performs?filename?expansion #+?????????????on?expressions?that?globbing?recognizes.===== #?Missing?in?[list]?in?a?for?loop for?a doecho?-n?"$a?" done#??The?'in?list'?missing,?therefore?the?loop?operates?on?'$@' #+?(command-line?argument?list,?including?whitespace).====== for?((?EXP1;?EXP2;?EXP3?)) docommand1command2command3 done===== while?condition docommand done===== until?condition docommand done======== case?"$variable"?in?"$condition1")?command...?;;?"$condition2")?command...?;;?esac#?僅僅是匹配,而沒有賦值操作#?支持文件名?通配 "E"?|?"e"?)*?) [[:upper:]]???)?echo?"Uppercase?letter";; [0-9]?????????)?echo?"Digit";; [a-zA-Z]*)?return?$SUCCESS;;??#?Begins?with?a?letter?
轉(zhuǎn)載于:https://blog.51cto.com/skypegnu1/1624464
總結(jié)
以上是生活随笔為你收集整理的Linux基础:Shell脚本学习的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Pyqt 窗体间传值
- 下一篇: 多输入的等价类划分以及测试