shell腳本是包含一個或多個命令的文件。本章介紹如何建立這樣的文件並使其可執行。還涉及一些shell腳本相關的問題,好比如何命名、放置和執行文件。
本章假設你位於home目錄,你能夠經過以下命令查看:
html
$echo "$HOME" /home/test
使用pwd命令或PWD變量的當前目錄:
python
$echo "$PWD" /home/test $echo "$HOME" /home/test
若是你是否是在你的home目錄,cd能夠進入:linux
$pwd /home/backup $cd $pwd /home/test
推薦使用Bash 4.3以上版本,由於它修復了Shellshock漏洞。程序員
$echo Hello, World! Hello, World!
文件注意不要有命名衝突,且要放在shell能夠找到的地方。正則表達式
初學者常用test做爲腳本名,這樣會和內置命令衝突。用顯示命令類型信息的type命令能夠查看:
redis
$type test test is a shell builtin $ type -a test test is a shell builtin test is /bin/test test is /usr/bin/test
shell內置命令test用來測試文件類型和比較值,輸入test會執行該內置命令。一般情Unix命令名儘量短。 一個描述性單詞的輔音很或兩個單詞的首部常見(例如mv:move、ls:list、ps:process status、sed:stream
editor)。
咱們的腳本命名爲hw。許多shell程序員添加後綴,如.sh,這個不是必須的。shell
一般在PATH中尋找shell命令:數組
$printf "$PATH" /usr/local/bin:/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/opt/gradle/bin:/home/test/.local/bin:/home/test/bin
也可使用絕對或者相對路徑:安全
/home/test/bin/hw bin/hw
爲此須要增長目錄bin並在PATH中添加$HOME/binbash
mkdir bin PATH=$PATH:$HOME/bin
PATH的修改能夠放置在用戶目錄的.bash_profile .bashrc、 .profile等shell啓動時source的文件中,注意只對交互式shell有效,對腳本無效。
一般使用文本編輯器來建立程序,咱們這個簡單的腳本使用重定向建立便可:
$echo echo Hello, World! > bin/hw
大於號(>) 告訴shell將命令的輸出發送到指定的文件,而不是終端。運行:
$bash bin/hw Hello, World! $sh bin/hw Hello, World! $ls -l /bin/sh lrwxrwxrwx. 1 root root 4 Aug 1 2014 /bin/sh -> bash
能夠見sh是bash的一個符號鏈接。調用sh等於調用bash。
下面添加可執行權限後能夠直接調用腳本。
$chmod +x bin/hw $hw Hello, World! $bin/hw Hello, World! $/home/test/bin/hw Hello, World!
可用的工具備vi、e三、nano、emacs、gedit、geany、Wing Ide。我的最喜歡的時後面兩個。效果圖以下:
Wing IDE:
Geany
注意:在Windows的文本文件行結束用回車(CR)和換行(LF)。在Unix系統中只有換行符。Windows的文本編輯器移植到Unix可能須要刪除回車。
#!/bin/bash #: Title : hw #: Date : 2015-07-18 #: Author : "Xu.Rongzhong" <xurongzhong#126.com> #: Version : 1.0 #: Description : print Hello, World! #: Options : None printf "%s\n" "Hello, World!"
首行#!/bin/bash表示執行的解釋器。
參數是存儲值的實體,有三種參數:位置參數、特殊參數和變量。位置參數是命令行參數,用數字表示。特殊參數由shell設置,用於存儲狀態相關信息,好比參數數量和最後一個命令的退出碼(好比, *, #, and _)。變量有本身的名字。參數的值可由美圓符號加名字、數值或字符訪問,好比$三、$#或$HOME。能夠添加大括號,好比${10}, ${PWD}或${USER}。
$n:表示第幾個參數,$1 表示第一個參數,$2 表示第二個參數 ...
$0:程序的名稱
Bourne shell只能保存9個位置參數,$10解釋爲${1}0。兼容起見,bash訪問9以上的位置參數必須加大括號,如 ${15}。
shift會使位置參數左移,原來的$0會丟失。
#!/bin/bash #: Title : hello #: Date : 2015-07-18 #: Author : "Xu.Rongzhong" <xurongzhong#126.com> #: Version : 1.0 #: Description : print Hello, World! #: Options : None #: Description : print Hello and the first command-line argument printf "%s, %s!\n" "$0" "$1" printf "%s, %s!\n" "$2" "$3" shift printf "After shift:\n%s, %s!\n" "$2" "$3"
執行結果:
$hello World! 2 3 4 /home/test/bin/hello, World!! 2, 3! After shift: 3, 4!
$#: 傳遞給程序的總的參數數目
$?: 上一個代碼或者shell程序在shell中退出的狀況,若是正常退出則返回0,反之爲非0值。
$*: 傳遞給程序的全部參數組成的字符串。
$@: 以"參數1" "參數2" ... 形式保存全部參數
$$: 本程序的(進程ID號)PID
$!: 上一個命令的PID
$-: 操做標識
#!/bin/bash #: Title : hello #: Date : 2015-07-18 #: Author : "Xu.Rongzhong" <xurongzhong#126.com> #: Version : 1.0 #: Description : print Hello, World! #: Options : None #: Description : print Hello and the first command-line argument printf "Arguments number: %s\n" "$#" printf "Exit code of last command: %s\n" "$?" printf "All arguments in string: %s\n" "$*" printf "All arguments in list: %s\n" "$@" printf "Current process ID: %s\n" "$$" printf "Last process ID: %s\n" "$!" printf "Flag: %s\n" "$-"
執行結果:
$special_arguments 1 2 3 hello hw special_arguments Arguments number: 3 Exit code of last command: 0 All arguments in string: 1 2 3 All arguments in list: 1 All arguments in list: 2 All arguments in list: 3 Current process ID: 28157 Last process ID: Flag: hB
變量標識符的定義和python相似,不作贅述。
name=VALUE
注意:等號的先後不能有空格。shell設置的變量大多爲大寫,好比:HOME、PWD和PATH,但有兩個例外:auto_resume和histchars。
在命令後輸入是參數,他們用空白(一個或多個空格或TAB)分割。引號中或轉移的空白不會有分割做用。
下面的命令都有四個參數:
$echo 1 '2 3' 4 5 1 2 3 4 5 $echo -n Now\ is the time Now is the time]$printf "%s %s\n" one two three one two three
命令行參數有長參數和短參數:
$ echo -n hello echo -n hello hello $ bash --version bash --version GNU bash, version 4.2.45(1)-release (x86_64-redhat-linux-gnu) Copyright (C) 2011 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> This is free software; you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law.
printf FORMAT ARG…
FORMAT中能夠包含普通、轉義和格式化符號。
\a:警報(鐘聲)
\b:退格
\e:Escape字符
\f:進紙
\n:換行符
\r:回車
\t:水平製表
\v:垂直選項卡
\\:反斜槓
\nnn:一到三個八進制數字指定的字符
\xHH:由一個或兩個十六進制數字指定的字符
格式化符前面有百分號。可選修改福可能置於兩個字符之間。參數比格式字符串多時,格式字符串會重用,直到全部的參數消耗完。常見的有%s, %d,%f和%x。
%s表示字符串:
$ printf "%s\n" Print arguments on "separate lines" Print arguments on separate lines
%b和%s相似,可是會轉義參數。
$ printf "%b\n" "Hello\nworld" "12\tword" Hello world 12 word $ printf "%s\n" "Hello\nworld" "12\tword" Hello\nworld 12\tword
%d表示整數:
$ printf "%d\n" 23 45 56.78 0xff 011 23 45 bash: printf: 56.78: invalid number 56 255 9
可見整數有十進制、八進制和十六進制兩種表示方法,當參數不是整數時會報錯,可是不會終止執行。
%f表示浮點數,默認保留小數點後六位:
$ printf "%f\n" 12.34 23 56.789 1.2345678 12.340000 23.000000 56.789000 1.234568
%e能夠顯示科學計數法:
$ printf "%e\n" 12.34 23 56.789 123.45678 1.234000e+01 2.300000e+01 5.678900e+01 1.234568e+02
%x和%X分別能夠用大小寫顯示十六進制數據。
$printf "color: #%02x%02x%02x;\n" 65 105 225 color: #4169e1;
在百分號後面添加數值傑克,負數表示左對齊,正數表示右對齊,0則表示用0填充。
$ printf "%8s %-15s:\n" first second third fourth fifth sixth first second : third fourth : fifth sixth : $ printf "%04d\n" 12 23 56 123 255 0012 0023 0056 0123 0255
浮點數的寬度針對字符串表示最大長度,針對浮點則表示小數點位數。
$printf "%12.4s %9.2f\n" John 2 Jackson 4.579 Walter 2.9 hellohellohello 1234567890.9797 John 2.00 Jack 4.58 Walt 2.90 hell 1234567890.98
上例最後一個例子,"hellohellohello"被截斷成4位,浮點數則不會截斷。
下面咱們展現一個銷售報表的實例:
#!/bin/bash #: Title : Sales Report #: Date : 2015-07-20 #: Author : "Xu.Rongzhong" <xurongzhong#126.com> #: Version : 1.0 #: Description : print formatted sales report #: Options : None ## Build a long string of equals signs divider===================================== divider=$divider$divider ## Format strings for printf header="\n %-10s %11s %8s %10s\n" format=" %-10s %11.2f %8d %10.2f\n" ## Width of divider totalwidth=44 ## Print categories printf "$header" ITEM "PER UNIT" NUM TOTAL ## Print divider to match width of report printf "%$totalwidth.${totalwidth}s\n" "$divider" ## Print lines of report printf "$format" \ Chair 79.95 4 319.8 \ Table 209.99 1 209.99 \ Armchair 315.49 2 630.98
執行結果:
$sales_report ITEM PER UNIT NUM TOTAL ============================================ Chair 79.95 4 319.80 Table 209.99 1 209.99 Armchair 315.49 2 630.98
注意上例中的${totalwidth},由於後面接了字符s,可能會混淆,因此加上大括號,一般加上大括號是個良好的習慣。 同時和Python同樣,行尾的反斜槓能夠用於續行。
在printf中使用-v參數能夠打印到變量:
$ printf -v num4 "%04d" 4
$ printf "%s\n" "$num4"
0004
在Unix中一切都是字節流,做爲文件訪問,有三個特殊流不多做爲文件名訪問,即每一個命令有的標準輸入stdin,標準輸出stdout和標準錯誤stderr。文件描述符對應0,1和2。I/O能夠來自或重定向到文件或管道。
文件 | 文件描述符 |
輸入文件—標準輸入 | 標準輸入是文件描述符0。它是命令的輸入,缺省是鍵盤,也能夠是文件或其餘命令的輸出。 |
輸出文件—標準輸出 | 標準輸出是文件描述符1。它是命令的輸出,缺省是屏幕,也能夠是文件。 |
錯誤輸出文件—標準錯誤 | 標準錯誤是文件描述符2。這是命令錯誤的輸出,缺省是屏幕,一樣也能夠是文件。 |
重定向用於修改默認的輸入和輸出。使用">"時,若是文件不存在,則建立。若是文件存在,會清空。以下方式能夠清空文件:
$printf "" > FILENAME $> FILENAME
重定向錯誤:錯誤能夠重定向到標準輸出等。
Command >&m | 把標準輸出重定向到文件描述符m中 |
Command < &- | 關閉標準輸入 |
Command 0>&- | 同上 |
$ printf '%s\n%v\n' OK? Oops! > FILE 2> ERRORFILE $cat ERRORFILE -bash: printf: `v': invalid format character
/dev/null表明空設備文件,沒用的東東均可以往這裏扔。
$printf '%s\n%v\n' OK? Oops! 2>/dev/null OK?
經過2>&1能夠把錯誤輸出到正常輸出,注意"2>&1"必定要在" > FILE"後面,不然錯誤輸出到屏幕上。
$printf '%s\n%v\n' OK? Oops! > FILE 2>&1
上述代碼bash中有快捷方式:"&> FILE",可是不適用於其餘shell。一樣的有">>",不會清空源文件,也有"&>> FILE"。
excec能夠修改重定向:
exec 1>tempfile exec 0<datafile exec 2>errorrfile
輸入重定向小結:
Command < filename > filename2 | Command命令以filename文件做爲標準輸入,以filename2文件做爲標準輸出 |
Command < filename | Command命令以filename文件做爲標準輸入 |
Command << delimiter | 從標準輸入中讀入,直接遇到delimiter分界符 |
輸出重定向小結:
Command > filename | 把標準輸出重定向到一個新文件中 |
Command >> filename | 把標準輸出重定向到一個文件中(追加) |
Command 1> filename | 把標準輸出重定向到一個文件中 |
Command > filename 2>&1 | 把標準輸出和錯誤一塊兒重定向到一個文件中 |
Command 2 > filename | 把標準錯誤重定向到一個文件中 |
Command 2 >> filename | 把標準輸出重定向到一個文件中(追加) |
Command >> filename2>&1 | 把標準輸出和錯誤一塊兒重定向到一個文件(追加) |
read是讀取標準輸入的內置命令。默認狀況下,它讀取直到換行符。輸入存儲一個或多個變量。
read var
若是有一個以上的變量,第一個字分配給第一變量,第二個字被分配給第二個變量,以此類推,最後一個變量接收全部剩餘字:
$ read a b c d January February March April May June July August $ echo $a bash: $: command not found... $ echo $a January $ echo $b February $ echo $c March $ echo $d April May June July August $printf "%s\n" "$d" April May June July August
讀bash的版本有幾個選項。只有-r選項POSIX標準被承認,表示不轉義。
$read b \\ $echo $b \ $read -r a \\ $echo $a \\
重定向至文件:
read var < FILENAME
管道(Pipeline)是原始的軟件管道:便是一個由標準輸入輸出連接起來的進程集合,上一個進程的輸出(stdout)直接做爲下一個進程的輸入(stdin)。bash用"|"表示管道。
$ printf "%s\n" "$RANDOM" "$RANDOM" "$RANDOM" "$RANDOM" | tee FILENAME 23173 30110 30212 3056 $ cat FILENAME 23173 30110 30212 3056
tee命令讀取標準輸入並將其傳遞到一個或多個文件到標準輸出, 另有-a參數表示追加到文件末尾。 $RANDOM是返回0-32,767之間的整數的bash變量。
命令替換:容許你使用命令的標準輸出就好像它是一個變量值同樣。其語法爲:$(UNIX command)。另外有種遺留語法,使用反引號(`,又名重音符)
#!/bin/bash #: Title : Command Substitution #: Date : 2015-07-18 #: Author : "Xu.Rongzhong" <xurongzhong#126.com> #: Version : 1.0 #: Description : print Hello, World! #: Options : None #: Description : print Hello and the first command-line argument printf "`date`\n" printf "$( date )\n"
執行結果:
$ cmd_sbst Sat Jul 18 16:41:34 CST 2015 Sat Jul 18 16:41:34 CST 2015
在shell中,有三種類型的循環(while, until和for)和三種條件執行(if, case和控制操做符&& and ||,即AND和OR)。
上一命令執行成功時,$?返回0,不然爲1到255之間的正整數,失敗的命令一般返回1。
$ echo $?
1
$ mkdir /qwerty
mkdir: 沒法建立目錄"/qwerty": 文件已存在
$ echo $?
1
測試表達式有test命令和非標準的關鍵字:[[ 和 ((。test命令比較字符串、整數和各類文件屬性; ((測試算術表達式,[[和((相似且增長了正則表達式比較。注意括號和其餘字符之間必定要有空格。
單箇中括號對和test是等價的。
-e 文件名:若是文件存在則爲真,另有非標準參數-a實現相似功能。
-r 文件名:若是文件存在且可讀則爲真
-w 文件名:若是文件存在且可寫則爲真
-x 文件名:若是文件存在且可執行則爲真
-s 文件名:若是文件存在且不爲空
-d 文件名:若是文件存在且爲目錄則爲真
-f 文件名:若是文件存在且爲普通文件則爲真
-c 文件名:若是文件存在且爲字符型特殊文件則爲真
-b 文件名:若是文件存在且爲塊特殊文件則爲真
-h -L 文件名:若是文件存在且爲塊特殊文件則爲真
$ test -h /etc/rc.local $ echo $? 1 $ test -h hello $ echo $? 0 $ [ -x "$HOME/bin/hw" ] $ echo $? 0 $ [[ -s $HOME/bin/hw ]] $ echo $? 0 $ [ -s $HOME/bin/hw ] $ echo $? 0
-eq:等於則爲真
-ne:不等於則爲真
-gt:大於則爲真
-ge:大於等於則爲真
-lt:小於則爲真
-le:小於等於則爲真
test 1 -eq 1 $ echo $? 0 $ [ 2 -eq 1 ] $ echo $? 1 $ [ 2 -ne 1 ] $ echo $? 0
字符串測試
= 等於則爲真。這個地方比較怪異,其餘語言都是==表示判斷是否相等,bash也支持==,可是竟然不是標準用法。
!= 不相等則爲真。
-z字串 字串長度僞則爲真。
-n字串 字串長度不僞則爲真。
$a=1 $b=1 $test "$a" = "$b" $echo $? 0 $[ "$a" != "$b" ] $echo $? 1 $ [ -z "" ] $ echo $? 0 $ test -n "" $ echo $? 1 $ str1=abc $ str2=def $ test "$str1" \< "$str2" $ echo $? 0 $ test "$str1" \> "$str2" $ echo $? 1
注意大於和小於符號因爲和重定向有衝突,須要轉義。
[[...]]和test相似,有所加強。但不是內置命令,參數可擴展,但不執行分詞和文件擴展名,且不是標準的,建議儘可能使用test。
加強:使用=~能夠匹配正則表達式;支持&&和||。
# string=whatever # [[ $string =~ h[aeiou] ]] #echo $? 0 # [[ $string =~ h[aeiou]i ]] #echo $? 1
非標準特徵。若是算術表達式求值爲零返回true,不然返回真。和以下方式等價:
test $(( a - 2 )) -ne 0 [ $a != 0 ]
使用shell語法,而不是內置的命令,大於和小於不須要轉義。好比:
if (( total > max )); then : ...; fi ((verbose)) && command ## execute command if verbose != 0
非數值當作0來處理。
$y=yes $((y)) && echo $y || echo n n
語法:
if <condition list> then <list> fi
若是<condition list>爲 true則執行<list>。
執行<條件列表>成功:
#!/bin/bash #: Title : Read Check #: Date : 2015-07-25 #: Author : "Xu.Rongzhong" <xurongzhong#126.com> #: Version : 1.0 #: Description : Read and Check Input #: Options : None read name if [[ -z $name ]] then echo "No name entered" >&2 exit 1 ## Set a failed return code fi
執行結果:
$read_check No name entered $read_check test
else能夠在爲false的時候執行:
#!/bin/bash #: Title : Integer Check #: Date : 2015-07-25 #: Author : "Xu.Rongzhong" <xurongzhong#126.com> #: Version : 1.0 #: Description : Integer Check #: Options : None printf "Enter a number not greater than 10: " read number if (( number > 10 )) then printf "%d is too big\n" "$number" >&2 exit 1 else printf "You entered %d\n" "$number" fi
執行結果:
$integer_check Enter a number not greater than 10: 11 11 is too big $integer_check Enter a number not greater than 10: 3 You entered 3
多個條件判斷可使用elif:
#!/bin/bash #: Title : multi if #: Date : 2015-07-25 #: Author : "Xu.Rongzhong" <xurongzhong#126.com> #: Version : 1.0 #: Description : Demo elif #: Options : None printf "Enter a number between 10 and 20 inclusive: " read number if (( number < 10 )) then printf "%d is too low\n" "$number" >&2 exit 1 elif (( number > 20 )) then printf "%d is too high\n" "$number" >&2 exit 1 else printf "You entered %d\n" "$number" fi
執行結果
$if3 Enter a number between 10 and 20 inclusive: 3 3 is too low $if3 Enter a number between 10 and 20 inclusive: 23 23 is too high $if3 Enter a number between 10 and 20 inclusive: 20 You entered 20
AND和OR的執行順序從左至右。若是前面的命令成功就會執行AND後面部分。若是前面部分部分執行失敗就會執行OR後面部分。
&&的應用實例:若是目錄存在就進入。
test -d "bin" && cd "bin"
||的應用實例:若是目錄不存在就返回1退出。
$cd "bin" || exit 1 -bash: cd: bin: No such file or directory logout
更多組合實例:
mkdir "$HOME/bin" && cd "$HOME/bin" || exit 1 if [ -d "$dir" ] && cd "$dir" then echo "$PWD" fi
case
case語句針對單詞(一般是變量)選擇模式並執行該模式相關聯的命令。模式中可使用通配符模式(*和)和字符列表和區間([...])。
語法:
case $1 in *"$2"*) true ;; *) false ;; esac
一個常見實例是肯定字符串是否包含在另外一個字符串中。它比用grep快,由於grep須要建立新的進程:
case $1 in *"$2"*) true ;; *) false ;; esac
另外一常見的任務是檢查字符串是不是有效的數字。
case $1 in *[!0-9]*) false;; *) true ;; esac
也能夠用於參數檢查:
case $# in 3) ;; ## We need 3 args, so do nothing *) printf "%s\n" "Please provide three names" >&2 exit 1 ;; esac
shell提供三種類型的循環:while, until和for。
語法:
while <list> do <list> done
實例:
#!/bin/bash #: Title : Simple While #: Date : 2015-07-27 #: Author : "Xu.Rongzhong" <xurongzhong#126.com> #: Version : 1.0 #: Description : Simple While with incrementing a variable #: Options : None n=1 while [ $n -le 10 ] do echo "$n" n=$(( $n + 1 )) done
執行結果:
$while1 1 2 3 4 5 6 7 8 9 10
true能夠用來建立無限循環:
while true ## ':' can be used in place of true do read x done
while能夠逐行讀取數據:
while IFS= read -r line do : do something with "$line" done < FILENAME?
不多使用,循環到條件失敗,和while相反:
n=1
until [ $n -gt 10 ]
do
echo "$n"
n=$(( $n + 1 ))
done
迭代模式:
for var in Canada USA Mexico
do
printf "%s\n" "$var"
done
另有非標準C語言格式:
for (( n=1; n<=10; ++n ))
do
echo "$n"
done
用於退出循環
while :
do
read x
[ -z "$x" ] && break
done
後面加數字還能夠跳出多級循環,好比:
for n in a b c d e
do
while true
do
if [ $RANDOM -gt 20000 ]
then
printf .
break 2 ## break out of both while and for loops
elif [ $RANDOM -lt 10000 ]
then
printf '"'
break ## break out of the while loop
fi
done
done
echo
退出當次循環
for n in {1..9} ## See Brace expansion in Chapter 4
do
x=$RANDOM
[ $x -le 20000 ] && continue
echo "n=$n x=$x"
done
魔線科技(深圳)有限公司測試部 shell培訓教程
公司大量招收測試人員,1-3年經驗,正規統招本科學歷,英語四級, 具有測試基礎,有python或linux簡單使用經驗優先,薪水:5-15k 本科如下特別優秀者能夠考慮 簡歷接收:xu.rongzhong#moxiangroup.com. 方向:互聯網後臺測試、自動化測試、性能測試、安全測試等。