shell中的eval學習與應用

1、bash命令處理的12個步驟;
php

一、將命令行分紅由固定元字符集分隔的記號;

SPACE, TAB, NEWLINE, ; , (, ),<, >, |,&node


記號類型包括單詞,關鍵字,I/O重定向符和分號。

二、檢測每一個命令的第一個記號,查看是否爲不帶引號或反斜線的關鍵字。

若是是一個開放的關鍵字,如if和其餘控制結構起始字符串,function,{或(,則命令實際上爲一複合命令。shell在內部對複合命令進行處理,讀取下一個命令,並重復這一過程。若是關鍵字不是複合命令起始字符串(如then等一個控制結構中間出現的關鍵字),則給出語法錯誤信號。

三、依據別名列表檢查每一個命令的第一個關鍵字;

若是找到相應匹配,則替換其別名定義,並退回第一步;不然進入第4步。該策略容許遞歸別名,還容許定義關鍵字別名。如aliasprocedure=function

四、執行大括號擴展,例如a{b,c}變成ab ac


五、若是~位於單詞開頭,用$HOME替換~。

使用usr的主目錄替換~user。

六、對任何以符號$開頭的表達式執行參數(變量)替換;


七、對形式$(string)的表達式進行命令替換;

這裏是嵌套的命令行處理。

八、計算形式爲$((string))的算術表達式;


九、把行的參數,命令和算術替換部分再次分紅單詞,此次它使用$IFS中的字符作分割符而不是步驟1的元字符集;
linux

 

十、對出現*, ?, [ / ]對執行路徑名擴展,也稱爲通配符擴展;程序員

十一、按命令優先級表(跳過別名),進行命令查尋;shell

十二、設置完I/O重定向和其餘操做後執行該命令。
數據庫

2、關於引用編程

一、單引號跳過了前10個步驟,不能在單引號裏放單引號
二、雙引號跳過了步驟1~5,步驟9~10,也就是說,只處理6~8個步驟。bash

也就是說,雙引號忽略了管道字符,別名,~替換,通配符擴展,和經過分隔符分裂成單詞。
雙引號裏的單引號沒有做用,但雙引號容許參數替換,命令替換和算術表達式求值。能夠在雙引號裏包含雙引號,方式是加上轉義符"\",還必須轉義$,`, \。
函數

3、eval的做用;學習

eval的做用是再次執行命令行處理,也就是說,對一個命令行,執行兩次命令行處理。這個命令要用好,就要費必定的功夫。我舉兩個例子,拋磚引玉。

一、例子1:用eval技巧實現shell的控制結構for

用eval技巧實現shell的控制結構for。

 

[root@home root]# cat myscript1
#!/bin/sh
evalit(){
       if [ $cnt = 1 ];then
               eval $@
               return
       else
               let cnt=cnt-1
               evalit $@
       fi
       eval $@
}
cnt=$1
echo $cnt | egrep "^[1-9][0-9]*$" >/dev/null
if [ $? -eq 0 ]; then
       shift
       evalit $@
else
       echo 'ERROR!!! Check your input!'
fi
[root@home root]# ./myscript1 3 hostname
home
home
home
[root@home root]# ./myscript1 5 id |cut -f1 -d' '
uid=0(root)
uid=0(root)
uid=0(root)
uid=0(root)
uid=0(root)


注意:bash裏有兩個很特殊的變量,它們保存了參數列表。

 

$*,保存了以$IFS指定的分割符所分割的字符串組。
$@,原樣保存了參數列表,也就是"$1""$2"...

這裏我使用了函數遞歸以及eval實現了for結構。
當執行eval $@時,它經歷了步驟以下:
第1步,分割成eval $@
第6步,擴展$@爲hostname
第11步,找到內置命令eval
重複一次命令行處理,第11步,找到hostname命令,執行。

注意:也許有人想固然地認爲,何須用eval呢?直接$@來執行命令就能夠了嘛。

例子2:一個典型錯誤的例子

錯誤!這裏給個典型的例子你們看看。

 

[root@home root]# a="id | cut -f1 -d''"
[root@home root]# $a
id:無效選項 -- f
請嘗試執行‘id --help’來獲取更多信息。
[root@home root]# eval $a
uid=0(root)


若是命令行復雜的話(包括管道或者其餘字符),直接執行$a字符串的內容就會出錯。分析以下。
$a的處理位於第6步──參數擴展,也就是說,跳過了管道分析,因而"|", "cut", "-f1","-d"都變成了id命令的參數,固然就出錯啦。
但使用了eval,它把第一遍命令行處理所得的"id", "|", "cut", "-f1","-d"這些字符串再次進行命令行處理,此次就能正確分析其中的管道了。

 

總而言之:要保證你的命令或腳本設計能正確經過命令行處理,跳過任意一步,均可能形成意料外的錯誤!

例子3:設置系統的ls色彩顯示

 

eval $(dircolors -b/etc/dircolors)


eval語句通知shell接受eval參數,並再次經過命令行處理的全部步驟運行它們。
它使你能夠編寫腳本隨意建立命令字符串,而後把它們傳遞給shell執行;
$()是命令替換,返回命令的輸出字符串。
其中dircolors命令根據/etc/dircolors配置文件生成設置環境變量LS_COLORS的bash代碼,內容以下

[root@localhost root]# dircolors -b> tmp
[root@localhost root]# cat tmp
LS_COLORS='no=00:fi=00:di=01;34:ln=01; ......
export LS_COLORS
#這裏我沒有指定配置文件,因此dircolors按預置數據庫生成代碼。
其輸出被eval命令傳遞給shell執行。


eval是對Bash Shell命令行處理規則的靈活應用,進而構造"智能"命令實現複雜的功能。
上面說起的命令是eval其中一個很普通的應用,它重複了1次命令行參數傳遞過程,純粹地執行命令的命令。
其實它是bash的難點,是高級bash程序員的必修之技。

 

4、命令優先級表
一、別名
二、關鍵字
三、函數
四、內置命令
五、腳本或可執行程序($PATH)



5、鑑於一些學習中會遇到的困惑,我再給出一些有趣的命令。

一、command builtinenable

上面的命令行說起過,第11步會進行命令查找,那它的具體過程如何呢?
它的默認查找次序爲函數,內部命令,腳本和可執行代碼。咱們每每要在實際編程中跳過一些查找項以知足必定的功能需求。這時候就要用到這三個命令來施展魔法~~

二、command

跳過別名和函數的查找,換句話說,它只查找內部命令以及搜索路徑中找到的腳本或可執行程序。
這裏舉個有趣的例子。

 

[root@home root]# type -all pwd
pwd is a shell builtin
pwd is /bin/pwd
[root@home root]# cat myscript2
#!/bin/sh
pwd(){
       echo "This is the current directory."
       command pwd
}
pwd
[root@home root]# ./myscript2
This is the current directory.
/root

 

我用pwd()函數取代了內置命令pwd以及外部命令/bin/pwd,而後在腳本里執行內置命令pwd。在這裏咱們爲何要用command呢?是爲了不函數陷入遞歸循環,由於函數名與內置命令同名,而函數的優先級比內置命令高。

三、builtin

顧名思義,它只查找內置命令。這個命令很簡單,就很少說了。

四、enable

與builtin相反,它屏蔽一個內置命令,容許運行一個shell腳本或同名的可執行代碼而無須給出徹底路徑名。
舉個例子吧。

pwd命令有兩個,一個是shell內置的,一個是可執行程序。

當執行一些奇怪的路徑名後,shell內置的pwd會打印出"錯誤信息",但外部的pwd會打印出當前目錄的"原來面目"。請看下面:

[root@home root]# cd //
[root@home //]# pwd
//
[root@home //]# type -all pwd
pwd is a shell builtin
pwd is /bin/pwd
[root@home //]# /bin/pwd
/
[root@home //]# enable -n pwd
[root@home //]# pwd
/

 

這樣,用enable -n屏蔽內置pwd命令後,就能夠用外部pwd打印出正確的路徑名了。

Bash博大精深,但願你們好好學習。:)

6、關於本文

本文是home_king兄發在LinuxSir.Org 討論區的一個專題《【Bas命令行處理】[詳解]》,我看這篇文檔寫的很不錯,適用新手,就整理出來了,並對段落進行了相應的排版和格式化,以方便你們閱讀;

轉自:http://www.linuxsir.org/main/?q=node/134

1. eval command-line

其中commandline是在終端上鍵入的一條普通命令行。然而當在它前面放上eval時,其結果是shell在執行命令行以前掃描它兩次。如:

pipe="|"

eval ls $pipe wc -l

shell1次掃描命令行時,它替換出pipe的值|,接着eval使它再次掃描命令行,這時shell把|做爲管道符號了。

若是變量中包含任何須要shell直接在命令行中看到的字符(不是替換的結果),就可使用eval。命令行結束符(;| &),Io重定向符(< >)和引號就屬於對shell具備特殊意義的符號,必須直接出如今命令行中。

2. eval echo \$$# 取得最後一個參數

如:cat last

eval echo \$$#

./last one two three four

four

第一遍掃描後,shell把反斜槓去掉了。當shell再次掃描該行時,它替換了$4的值,並執行echo命令

3.如下示意如何用eval命令建立指向變量的「指針」:

x=100

ptrx=x

eval echo \$$ptrx指向ptrx,用這裏的方法能夠理解b中的例子

100 打印100

eval $ptrx=5050存到ptrx指向的變量中。

echo $x

50 打印50

相關文章
相關標籤/搜索