歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Unix知識 >> Unix基礎知識 >> Unix基礎教程(6)

Unix基礎教程(6)

日期:2017/2/25 10:12:33   编辑:Unix基礎知識

第6章 B-Shell及編程

【例6-1】使用#!為腳本文件自設定解釋程序。

$ cat lsdir

#!/bin/od -c

if [ $# = 0 ]

then

dir=.

else

dir=$1

fi

find $dir -type d -print

$ chmod u+x lsdir

$ ./lsdir

0000000 # ! / b i n / o d - c \n i f

0000020 [ $ # = 0 ] \n t h e n

0000040 \n d i r = . \n e l s e \n

0000060 d i r = $ 1 \n f i \n f i n d $

0000100 d i r - t y p e d - p r i

0000120 n t \n \n

腳本文件中第一行如果缺少了#!,那麼,系統就會用默認的shell程序來解釋執行腳本文件的文本。一般系統的默認shell是/bin/sh,LINUX的默認shell是/bin/bash。

有三種方法可以執行腳本文件。

(1) sh < lsdir

(2) sh lsdir

sh lsdir /bin

(3) chmod u+x lsdir 給文件lsdir可執行屬性

./lsdir

【例6-2】標准輸入重定向的使用舉例。

./myap < try.in

如果你在編寫一個程序btree.c完成一個數據結構的作業,根據一棵二叉樹的先序,中序,求後序。程序運行的時候需要輸入先序序列,中序序列,然後打印出後序序列。由於程序邏輯較復雜,先序序列和後序序列分別為下列順序時,程序運行不正確。

ABFGEHILMOPCDJKNQR

FGBHELIOPMACJDQNRK

這樣,就需要反復地修改程序文件btree.c,編譯後運行./btree,然後根據程序提示,從鍵盤輸入上述的兩個字符串數據。反復的調試需要反復的輸入上述數據,這種重復性的勞動效率很低,而且容易出錯。這樣,就可以將上述兩行文本編輯成文件btree.tst1, 利用shell的輸入重定向,運行./btree < btree.tst1。

【例6-3】簡單的Here document。

cat << TEXT

**********

* Hello! *

**********

TEXT

上述命令執行結果,在屏幕上顯示:

**********

* Hello! *

**********

這裡,<<符號後面是定界符,在shell輸入中下一個TEXT之前的這段文本,算作cat命令的標准輸入。

【例6-4】在Here document中進行命令替換和變量替換。

cat << TOAST

Hello! Time: `date`

My Home Directory is $HOME

Bye!

TOAST

上述命令執行結果為:

Hello! Time: Sat Jul 27 14:47:56 BEIJING 2004

My Home Directory is /usr/jiang

Bye!

【例6-5】在Here document中禁止命令替換和變量替換。

cat << \TOAST

Hello! Time: `date`

My Home Directory is $HOME

Bye!

TOAST

cat << 'TOAST'

Hello! Time: `date`

My Home Directory is $HOME

Bye!

TOAST

上述兩種情況,輸出結果都是:

Hello! Time: `date`

My Home Directory is $HOME

Bye!

【例6-6】B-shell的標准錯誤輸出重定向舉例。

(1) cc myap.c -o myap 2> myap.err

將cc命令的stderr重定向到文件myap.err中。

(2) 設try是程序員設計的某個應用程序。

try > try.out 2>try.err

try 1> try.out 2>try.err

將try程序執行後的stdout或stderr分別定向到兩個不同的文件中(其中的1和2,是文件描述符,分別是stdout和stderr,如果換成其它數字也可以對try的其它文件描述符輸出改向)。

【例6-7】B-shell指定文件描述符的輸出重定向。

myap >rpt 2>&1

或者:

myap 1>rpt 2>&1

【例6-8】B-shell的管道處理舉例。

(1) ls -l | grep ’^d’

前一命令的stdout作後一命令的stdin。

(2) cc myap.c -o myap 2>&1 | more

前一命令的stdout+stderr作為下一命令的stdin。這裡管道操作的優先級高,先完成cc的標准輸出管道到下個命令的標准輸入,然後,將文件描述符重定向的和文件描述符1一樣,就完成了將標准輸出和標准錯誤輸出都管道到下個命令的目的。

【例6-9】shell變量的定義和引用舉例。

定義一個名為addr的變量,存放一個IP地址字符串。

$ addr=20.1.1.254 (最左側的$為sh命令提示符)

$ echo $addr (引用變量addr,echo執行前,sh完成變量替換)

20.1.1.254

$ city="Beijing, China"

$ echo $city

Beijing, China

$ echo Connected to $proto Network

Connected to Network

$ proto=TCP/IP

$ echo Connected to $proto Network

Connected to TCP/IP Network

【例6-50】使用shell函數的例子。

這個例子是一個廣域網通信適配卡驅動程序安裝腳本的一部分。

這個通信適配卡安裝之前要求輸入硬件的中斷號,I/O基地址和通信速率。然後,列出操作員的輸入,等待確認後才執行安裝操作。

安裝操作在41~85行之間,被省略。

第4~16行的函數get_answer打印出一條消息,然後,強制輸入y或者n,否則繼續要求用戶輸入。函數體內使用了$1引用調用函數get_answer的命令行參數,用return使得shell函數象普通命令一樣有返回碼,供條件判斷使用。

第20~34行的函數get_val有四個參數。第一個參數$1存放用戶輸入值的shell變量的名字,第二個參數$2是輸入之前給用戶的提示信息,第三個參數$3是用戶直接按下回車時的缺省值,最後一個參數$4是允許取值的有效值列表。函數get_val強制用戶輸入一個有效值列表中的有效值,否則,就給出有效值列表做提示,並進一步要求用戶重新輸入。函數體內使用了while循環和for循環結構。第29行的break命令含有參數2,可以跳出兩層循環。

腳本程序執行時從第36行開始執行。由於腳本文件前面有了函數說明,shell記下了函數名字get_val和get_answer作為內部命令,在執行命令get_val和get_answer時,shell都轉去執行函數體,而不是到磁盤上尋找這樣名字的命令文件。

$ awk '{print NR,$0}' WanCom

1 # Shell function to read a Y/N response

2 # Usage: get_answer <message>

3 #

4 get_answer()

5 {

6 while true

7 do

8 echo "$1? (y/n) \c"

9 read yn

10 case $yn in

11 [yY]) return 0;;

12 [nN]) return 1;;

13 *) echo "Please answer y or n" ;;

14 esac

15 done

16 }

17 # Shell function to get a value

18 # Usage: get_val <var_name> <message> <default_val> <list>

19 #

20 get_val()

21 {

22 while true

23 do

24 echo "$2 [$3] : \c"

25 read val

26 [ "$val" = "" ] && val=$3

27 for i in $4

28 do

29 [ "$val" = "$i" ] && break 2

30 done

31 echo "**** Invalid choice $val, must be in $4"

32 done

33 eval "$1=$val"

34 }

35 # main program

36 get_val INTR "Interrupt Number" 10 "2 3 4 5 7 10 11 12 14 15"

37 get_val PORT "I/O Base Address" 320 "200 210 220 230 300 310 320 330"

38 get_val BAUD "Baud Rate" 9600 "2400 9600 14400 33600 64000"

39 echo "Interrupt $INTR, I/O base address $PORT, Baud rate is $BAUD"

40 get_answer "Do you want to install WanCom adapter driver" && {

41 echo "Please Wait ...\c"

……

85 }

$ ./WanCom

Interrupt Number [10] : 22

**** Invalid choice 22, must be in 2 3 4 5 7 10 11 12 14 15

Interrupt Number [10] : 14

I/O Base Address [320] :

Baud Rate [9600] : 33.6

**** Invalid choice 33.6, must be in 2400 9600 14400 33600 64000

Baud Rate [9600] : 33600

Interrupt 14, I/O base address 320, Baud rate is 33600

Do you want to install WanCom adapter driver? (y/n) y

Please Wait ...

...

$

【例6-51】shell腳本程序中-x開關的作用。

腳本程序中打開-x開關,可以觀察到程序執行的流程。

$ cat chmod1

set -x

echo "$*"

for i

do

[ -f $i ] && {

chmod a+r $i

eoho "$i is readable"

}

done

$ ./chmod1 a*

+ echo a1 aa8 abcd

a1 aa8 abcd

+ [ -f a1 ]

+ chmod a+r a1

+ echo a1 is readable

a1 is readable

+ [ -f aa8 ]

+ [ -f abcd ]

+ chmod a+r abcd

+ echo abcd is readable

abcd is readable

本例中第一行的set -x也可以省略,使用命令sh -x chmodl a*也能達到相同的效果。或者,將腳本文件chmod1的第一行修改為!/bin/sh -x也可以。

【例6-52】交互式shell中-x開關的作用。

交互式shell中打開-x開關,可以觀察shell的命令替換,變量替換,文件名生成,以及轉移符的作用。每次shell在真正執行一個命令之前都把要執行的命令顯示出來。

$ set -x

$ tty

+ tty

/dev/tty6

$ termno=`expr \`tty\` : /dev/tty\\\\\\(.\\*\\\\\\)`

+ + tty

+ expr /dev/tty6 : /dev/tty\(.*\)

termno=6

$ echo $termno

+ echo 6

6

$ find $HOME -size +100 \( -name \*.c -o -name xxxx \) -exec rm -i {} \;

+ find /usr/jiang -size +100 ( -name *.c -o -name xxxx ) -exec rm -i {} ;

【例6-53】shell的-u開關的使用。

可以檢查出引用變量時的變量名拼寫錯誤。

$ name=CSPTF

$ echo Connecting to $nmae Network

Connecting to Network

$ echo Connecting to $NAME Network

Connecting to Network

$ set -u

$ echo Connecting to $nmae Network

nmae: 0402-009 Parameter is not set.

$ echo Connecting to $NAME Network

NAME: 0402-009 Parameter is not set.

$ echo Connecting to $name Network

Connecting to CSPTF Network

$

【例6-54】使用set命令設置shell的位置變量。

使用set命令可以重新設置shell的位置變量,先前的位置變量全部被新的值代替。

$ echo $#

0

$ date

Sun Jul 28 11:00:40 BEIJING 2004

$ set `date`

$ echo $1 $2 $3 $4

Sun Jul 28 11:00:40

$ ls -l /etc/motd

-rw-r--r-- 1 root staff 316 Jan 5 08:42 /etc/motd

$ set `ls -l /etc/motd`

sh:-rw-r-r-: bad option(s)

$ set -- `ls -l /etc/motd`

$ echo $9:$5 $1

/etc/motd:316 -rw-r--r--

注意--的使用,顯式地指定set命令選項的結束,否則set會把以減號開頭的命令行參數理解成set的選項。關於--,在前面的4.4.4節介紹過。

【例6-55】逐個打印源程序文件。

打印多個源程序文件,每打印個文件之前列出文件名,打印文件時每行帶上行號。

$ cat prt

while [ $# -gt 0 ]

do

echo ===================

echo FILE NAME: $1

echo ===================

awk '{printf("2d %s\n",NR,$0)}' $1

shift

done

$ ./prt makefile *.[ch]

【例6-56】給出若干個程序名,終止這些程序文件啟動的所有進程。

獲取程序啟動的進程的PID的方法,在前面的“元字符”6.5.10節中介紹過。這裡的腳本程序中使用了位置變量和shift命令。

$ cat k

while [ $# != 0 ]

do

echo "$1: \c"

PID=`ps -e | awk "/[0-9]:[0-9][0-9] $1\\$/{printf(\\"%d \\",\\$1)}"`

if [ -n "$PID" ]

then

echo kill $PID

kill $PID

else

echo No process

fi

shift

done

$ ./k myap findkey sortdat

myap: kill 26506 38020

findkey: kill 31542

sortdat: No process

$

【例6-57】軟件安裝時調整操作系統內核的部分參數。

下面的一段腳本程序,在SCO UNIX系統中安裝某個軟件包時,調整操作系統內核參數。第一行和第二行列出了相應的參數和期望的配置值。例如:要求MSGMNB參數取值至少32768, NQUEUE參數取值至少64。

命令configure的格式:

configure -y 參數名

configure 參數名=參數值 參數名=參數值 ……

第一種格式,打印出指定名字的內核參數的當前取值,第二種格式,調整指定名字的內核參數為指定的參數值,並且可以一次調整多個參數。這個命令是SCO UNIX專用的,在其它UNIX中沒有通用性。

腳本程序使用set命令和shift命令對位置變量的影響。內核參數當前取值已經滿足要求的參數不再調整。

1 PARA="MSGMNB MSGTQL MSGSEG NBLK4096 NBLK2048 NBLK1024 NQUEUE"

2 VAL=" 32768 600 16384 16 128 100 64"

3 CHANGE=

4 cd /etc/conf/cf.d

5 set $VAL

6 for i in $PARA; do

7 x=`./configure -y $i`

8 if [ $x -lt $1 ]

9 then

10 echo "Adjusting parameter $i from $x to $1"

11 CHANGE="$i=$1 $CHANGE"

12 fi

13 shift

14 done

15 if [ -n "$CHANGE" ]

16 then

17 ./configure $CHANGE

18 fi

Copyright © Linux教程網 All Rights Reserved