Linux shell高級編程(上)

1. 概述

在前面的章節中,我們講解了shell腳本的基本編程,包括結構化命令,讀取用戶輸入和測試命令。而這篇文章主要是描述shell腳本的高級編程部分,包括創建函數,流式編輯器sed和gawk的基本用法。

 

 

2. 函數部分

 

(1)基本格式:

function name() {

 

commands

 

}

 

示例:

!/bin/bash
#using a function in a script
#格式:function name(){ command}
function func(){
echo "this is an example of a function"

}
count=1
while [ $count -le 5 ]
do
func
count=$[ $count+1]
done
echo "this is the end of the loop"

 

(2)使用變量接收函數輸出

當使用變量接收函數輸出,必須用反引號,而且在函數中要用echo語句生成消息。

result=`functionname`

function functionname() {

 

echo "message"

}

 

 

#!/bin/bash
#using the echo to return a value
function db1() {
read -p "Enter a value:" value
echo  $[ $value*2 ]  #使用echo生成消息,然後用變量接收函數值
}
result=`db1`
echo "the new value is $result

 

(3)向函數傳遞參數環境變量

使用標準的參數環境變量表示命令行給函數傳遞的參數,$0表示函數名,$1表示函數命令行的第一個參數,$2表示函數命令行的第二個參數。

由於函數調用是在腳本內部的,所以只能在腳本內部向函數傳遞參數。在腳本外部,如在命令行中,那麼傳遞的$1,$2是腳本參數變量,是向腳本傳遞的參數,此時,在函數內部是不能直接用的,因爲函數有自己的特有的參數環境變量。必須手動的通過調用函數時傳遞到函數內部纔可以使用。

腳本的參數環境變量雖然與函數的參數環境變量形式相同,但有着本質的區別。腳本的參數環境變量是腳本使用的,而在函數內部時,函數的參數環境變量是函數特有的。一個相當於全局變量,一個相當於局部變量,如果在函數中使用與腳本中相同的參數環境變量$1,那麼函數中的$1當然會覆蓋腳本中的$1,如果不向函數中手動傳值的話,這個變量就沒有值了。所以,必須向函數中傳遞$1,而不能直接用腳本中的$1.

 

直接使用腳本中的參數環境變量:

#!/bin/bash
#trying to access script parameters inside a function
#函數使用的變量$1和$2,不同於主腳本的$1和變量$2,如果想在函數>中使用這些值,那麼必須在調用該函數時手動傳入這些值
function func7(){
echo $[ $1*$2 ]

}
if [ $# -eq 2 ]
then
func7
echo "the result is $value"
else
echo "Usage:badtest a b"
fi

 

 

結果: [root@localhost chapter14]# ./test7 1 3
./test7: line 5: * : syntax error: operand expected (error token is "* ")

 

手動傳遞腳本中的參數環境變量

#!/bin/bash
#trying to access script parameters inside a function
#函數使用的變量$1和$2,不同於主腳本的$1和變量$2,如果想在函數>中使用這些值,那麼必須在調用該函數時手動傳入這些值
function func7(){
echo $[ $1*$2 ]

}
if [ $# -eq 2 ]
then
value=`func7 $1 $2`
echo "the result is $value"
else
echo "Usage:badtest a b"
fi

分析: fun7 $1 $2 把腳本中的參數環境變量手動傳遞到函數中去了。

 

 

(4)函數中的局部變量與全局變量

全局變量指的是在shell腳本內處處有效的變量。如果在腳本的主代碼中定義了全局變量,那麼在函數內部就可以獲得這個變量。

而局部變量的定義可以用 local temp

#!/bin/bash
#demonstrating the local keyword
#全局變量就是在shell腳本內處處有效的如果腳本的主代碼中定義了>全局變量,那麼函數內部可以獲得這個值,同樣要在函數內部定義了>全局變量,那麼腳本的主代碼也可以獲得這個值。而局部變量只在函>數的內部有效 local是用來定義局部變量


function fun(){
local temp=$[ $value+5 ]
result=$[ $temp*2 ]

}

temp=4;
value=6;
fun
echo "the result is $result"
if [ $temp -gt $value  ]
then
echo "temp is larger"
else
echo "temp is smaller"
fi

 

(5)向函數傳遞數組

向函數傳遞數組,必須將數組變量拆分爲單個元素,然後使用這此元素值作爲函數參數。函數內部可以將這些參數重組爲新的數組變量。

 

傳遞數組示例1:

#!/bin/bash
#array variable to function set
#向函數傳遞數組,首先將數組用$@取出來,然後在函數內部重新構建
數組,$@用來獲得所有的命令行參數
function test(){
local newarray
newarray=(`echo "$@"`) //$@代表傳遞過來的所有參數,然後重新構造數組
echo "the new array value is:${newarray[*]}"

}
myarray=(1,2,3,4,5)
echo "the original array is ${myarray[*]}"
test ${myarray[*]}  //傳遞數組元素

結果:

[root@localhost chapter14]# ./test11
the original array is 1,2,3,4,5
the new array value is:1,2,3,4,5

 

傳遞數組示例2:

#!/bin/bash
function add() {
local sum=0
local newarray
newarray=(`echo "$@"`)
for value in ${newarray[*]}
do
sum=$[ $sum+$value ]
done
echo $sum
}
myarray=(1 2 3 4 5)
echo "the original array is: ${myarray[*]}"
arg1=`echo ${myarray[*]}`
result=`add $arg1`
echo "the result is $result"

 

運行結果:

[root@localhost chapter11]# ./test111
the original array is: 1 2 3 4 5
the result is 15

 

返回數組:

 

#!/bin/bash
function add() {
local originarray
local newarray
local elements
local i
originarray=(`echo "$@"`)
newarray=(`echo "$@"`)
elements=$[ $#-1 ]
for((i=0;i<=$elements;i++)){
 newarray[$i]=$[ ${originarray[$i]}*2 ]

}
echo ${newarray[*]}

}
myarray=(1 2 3 4 5)
echo "the original array is:${myarray[*]}"
arg1=`echo ${myarray[*]}`
result=(`add $arg1`)
echo "the new array is:${result[*]}"

 

運行結果:

[root@localhost chapter11]# ./test112
the original array is:1 2 3 4 5
the new array is:2 4 6 8 10

 

 

(6)創建庫

可以用bash shell創建函數庫文件,然後可以在不同的腳本中引用該庫文件。

 

函數庫文件:

#!/bin/bash
#my script functions
#創建庫文件在腳本內部用點操作符來運行庫文件腳本,這樣腳本可以
使用這些函數
function addem(){
echo $[ $1+$2 ]

}
function multem(){
echo $[ $1*$2 ]

}
function divem(){
if [ $2 -ne 0 ]
then
echo $[ $1/$2 ]
else
echo -1
fi


}

 

在腳本中調用庫文件,用source命令,source命令在shell腳本內部運行庫文件腳本。source有一個短小的別名,即點操作符。

下面在腳本中調用該函數庫:

 

#!/bin/bash
#using functions defined in a library file
. ./myfuncs    //點操作符運行庫文件腳本
value1=10
value2=5
result1=`addem $value1 $value2`
result2=`multem $value1 $value2`
result3=`divem $value1 $value2`
echo "the result of adding them is:$result1"
echo "the result of multiplying them is $result2"
echo "the result of dividing them is:$result3"

結果:

[root@localhost chapter14]# ./test14
the result of adding them is:15
the result of multiplying them is 50
the result of dividing them is:2

 

 

將庫文件放到.bashrc,在root用戶中就放到/root/.bashrc中去。由於每次無論bash shell是交互式啓動還是從已有的shell啓動新的shell,都會在主目錄下查找這個文件。這樣就可以在新的shell中直接使用這個庫函數。

 

在 /root/.bashrc 中加入:

 

. /home/shell/myfuncs

 

然後在命令行中運行: source /root/.bashrc

[root@localhost chapter14]# addem 1 2
3
即看到,可以在shell命令行中任意調用函數庫中的函數了。

 

 

 

3. sed基礎知識

sed也稱爲流編輯器,它每次讀取一行數據,將該數據與所提供的編輯器命令進行匹配,根據命令修改數據流中的數據,然後將數據輸出到stdout中去。在流編輯器將全部命令和一行數據匹配完之後,它將讀取下一行數據,並重覆上面這個過程。處理完數據流中的全部數據之後,流編輯器停止。 gawk比sed更先進的地方是提供了一種編程語言而不僅僅是編輯器命令行。

 

 

(1)在命令行中定義編輯器命令

替換命令: s. s命令用第二個字符串去替換與文本匹配的兩斜槓之間的第一個字符串。

 

例子:

[root@localhost chapter14]# echo "this is a test"|sed 's/test/big test/'
this is a big test

 

分析: 管道的輸出作爲流編輯器sed的輸入。

 

如果從sed命令執行多個命令,只需要用-e選項,並且兩個命令之間用;隔開。

 

[root@localhost chapter14]# cat <data1
The quick brown fox jumps over the lazy dog.
The quick brown fox jumps over the lazy dog.
The quick brown fox jumps over the lazy dog.
The quick brown fox jumps over the lazy dog.
The quick brown fox jumps over the lazy dog.
The quick brown fox jumps over the lazy dog.
The quick brown fox jumps over the lazy dog.

 

[root@localhost chapter14]# sed -e 's/brown/green/; s/dog/cat/' data1
The quick green fox jumps over the lazy cat.
The quick green fox jumps over the lazy cat.
The quick green fox jumps over the lazy cat.
The quick green fox jumps over the lazy cat.
The quick green fox jumps over the lazy cat.
The quick green fox jumps over the lazy cat.
The quick green fox jumps over the lazy cat.

 

(2)從文件讀取編輯器命令

-f從指定文件讀取命令。

script1:

 

s/brown/green/
s/fox/cat/

[root@localhost chapter14]# sed -f script1 data1
The quick green cat jumps over the lazy dog.
The quick green cat jumps over the lazy dog.
The quick green cat jumps over the lazy dog.
The quick green cat jumps over the lazy dog.
The quick green cat jumps over the lazy dog.
The quick green cat jumps over the lazy dog.
The quick green cat jumps over the lazy dog.

 

 

(3)替換選項

a.替換標記

格式:

s/pattern/replacement/flags
替換標記flags:

數字: 表示新文本的替換模式

g: 表示用新文本替換現有文本的全部實例

p: 表示打印原始行的內容

w file:將替換結果寫入文件

 

 

示例:

 

[root@localhost chapter14]# cat <data4
This is a test of the test script
This is the second test of the test script

 

[root@localhost chapter14]# sed 's/test/trial/2' data4
This is a test of the trial script
This is the second test of the trial script

即將第2個test替換成trial.

 

[root@localhost chapter14]# sed 's/test/trial/g' data4
This is a trial of the trial script
This is the second trial of the trial script

替換所有的test成trial.

 

p命令是打印原始行的內容。

-n選項是禁止編輯器的輸出。

那麼p命令與-n選項使用就表示打印生成替換命令已經修改的那些行。

 

[root@localhost chapter14]# cat <data6
this is line1
this is line2
this is line3
this is line4

 

[root@localhost chapter14]# sed -n 's/line1/line/p' data6
this is line

 

-n禁止編輯器輸出

p打印已經修改的行。

所有 -n與p只打印已經替換的行。如果沒有-n,將會打印已經修改的行和編輯器的輸出。

 

[root@localhost chapter14]# sed  's/line1/line/p' data6
this is line
this is line
this is line2
this is line3
this is line4

 

 

b.替換字符:

正斜槓的替換。

 

sed 's///bin//bash///bin//csh/' /etc/passwd

 

必須使用反斜槓對正斜槓進行轉義。由於替換標記中有正斜槓。

也可以使用一個不同的字符串定界符。如!

sed 's!/bin/bash!/bin/csh!'  /etc/passwd

 

c.使用地址

如只替換第二行。

sed '2s/dog/cat/' data1

2表示只替換第二行。

 

sed '2,3s/dog/cat/' data1
2,3表示起始行與結束行。替換第2行與第3行。

 

 sed '2,$s/dog/cat/' data1

替換第2行到結束行。 2,$

 

 

d. 使用文本模式篩選器

/pattern/command

 

 sed -n  '/root/s/bash/csh/p' /etc/passwd

 

表示匹配/root/的替換。

 

e. 組合命令

如果在單獨一行上執行多個命令,用大括號將命令括在一起。

 

[root@localhost chapter14]# sed '2{
> s/fox/elep/
> s/dog/cat/
> }' data1

 

表示在2行上執行如下兩個命令。

 

 

(4)刪除行

刪除命令用d來完成。

sed '3d' data6

表示刪除第3行。

sed '2,3d' data6

表示刪除第2行與第3行。

 

(5)插入與附加文本

 

插入命令(i):在指定行之前添加一個新行

附加命令(a):在指定行之後添加一個新行

 

格式:

sed '[address]command/ new line'

 

[root@localhost chapter14]# echo "testing"|sed 'i/this is a test'
this is a test
testing

 

[root@localhost chapter14]# sed '3i/this is a test' data6
this is line1
this is line2
this is a test
this is line3
this is line4

 

 

[root@localhost chapter14]# sed '3a/this is a test' data6
this is line1
this is line2
this is line3
this is a test
this is line4

 

(6)更改行 c命令

[root@localhost chapter14]# sed '3c/this is a test' data6
this is line1
this is line2
this is a test
this is line4

更改第3行。

 

[root@localhost chapter14]# sed '/line3/c/this is a test' data6
this is line1
this is line2
this is a test
this is line4

查找到line3的行更改。

 

 

(7)打印命令

p用於打印文本行。

=用於打印行號

 

[root@localhost chapter14]# echo "this is a test"|sed 'p'
this is a test
this is a test
[root@localhost chapter14]# echo "this is a test"|sed -n 'p'
this is a test

 

第一個例子的第一行是sed輸出,第二行是p輸出的。

 

 

[root@localhost chapter14]# sed '=' data6
1
this is line1
2
this is line2
3
this is line3
4
this is line4

 

打印行號。

sed ''

(8)讀寫文件

[address] w filename

 

[root@localhost chapter14]# sed -n '1,2w test' data6

[root@localhost chapter14]# cat <test
this is line1
this is line2

將第1行與第2行寫入到test.

 

[address] r filename

 

 

[root@localhost chapter14]# sed '$r data11' data6
this is line1
this is line2
this is line3
this is line4
this is an added line.
this is the second added line.

從data11中讀出兩行加入到data6的最後一行。

 

4. gawk基礎知識

gawk程序的基本格式:

gawk options program file

-F fs 字段文件分隔符

-f file 指定讀取程序的文件名

-v var=value 定義gawk程序中使用的變量和默認值

-mf N 數據文件中要處理的字段的最大數目

-mr N 指定數據文件中的最大記錄大小

-W keyword 指定gawk兼容模式或警告級別

 

gawk的威力在於讀取一行數據,處理這些數據可以生成任意類型的報告。

 

(1)從命令行讀取程序腳本

gawk程序腳本必須由{}來定義。而且腳本必須在單引號之內。

 

[root@localhost chapter14]# gawk '{print "hello"}'
hello
hello
world
hello

 

輸入值時,始終打印出hello.

 

(2)從文件中讀取程序腳本

自動變量: $0,$1,$2,....

$0表示整個文本行

$1表示文本行的第一個數據字段

$2表示文本行的第二個數據字段

各數據字段依據字段分割符進行分割。gawk默認的分割符是空白字符如空格符或者是製表符。

 

 

如:

gawk -F: '{print $1}' /etc/passwd

 

-F: 指定字段分割符爲冒號。

 

script3:

 

{test="'s userid is"
 print $5 test $1
}

 

gawk -F: -f script3 /etc/passwd

 

-f指定讀取的程序名。

 

 

(3)BEGIN與END

BEGIN表示在處理數據之前運行的腳本。

END在讀取數據之後運行的腳本。

 

 

BEGIN {
print "the latest list of users and shells"
print " USERid          Shell"
PRINT "--------         ------"
FS=":"

}

{
print $1 "      " $7

}

END {

print "This concludes the listing"

}

 

運行結果:

awk -f script4 /etc/passwd
the latest list of users and shells
 USERid         Shell
root    /bin/bash
bin     /sbin/nologin
daemon  /sbin/nologin
adm     /sbin/nologin
lp      /sbin/nologin
sync    /bin/sync
shutdown        /sbin/shutdown
halt    /sbin/halt
mail    /sbin/nologin
news
uucp    /sbin/nologin
operator        /sbin/nologin
games   /sbin/nologin
gopher  /sbin/nologin
ftp     /sbin/nologin
nobody  /sbin/nologin
nscd    /sbin/nologin
vcsa    /sbin/nologin
rpc     /sbin/nologin
mailnull        /sbin/nologin
smmsp   /sbin/nologin
pcap    /sbin/nologin
ntp     /sbin/nologin
dbus    /sbin/nologin
avahi   /sbin/nologin
sshd    /sbin/nologin
rpcuser /sbin/nologin
nfsnobody       /sbin/nologin
haldaemon       /sbin/nologin
avahi-autoipd   /sbin/nologin
distcache       /sbin/nologin
apache  /sbin/nologin
postgres        /bin/bash
oprofile        /sbin/nologin
webalizer       /sbin/nologin
dovecot /sbin/nologin
squid   /sbin/nologin
mysql   /bin/bash
xfs     /sbin/nologin
named   /sbin/nologin

 

關於awk就簡單介紹到這了。

 

總結:主要介紹了shell高級編程中的函數部分,sed,與awk的簡單用法。要分清函數中的參數環境變量與腳本中的參數環境變量。還有awk中的自動變量,雖然寫法都是一樣,但是表示的意義完全不相同。關於sed,awk的高級用法下一篇文章中繼續介紹。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章