shell中eval的使用問題

轉載:http://www.blogjava.net/jasmine214--love/archive/2010/11/26/339106.htmlhtml

 

本文將會講解一些linux中命令的使用與技巧但願對新手給予幫助
一 eval命令將會首先掃描命令行進行全部的置換,而後再執行該命令。該命令適用於那些一次掃描沒法實現其功能的變量。該命令對變量進行兩次掃描。這些須要進行兩次掃描的變量有時被稱爲複雜變量。不過這些變量自己並不複雜。
eval命令也能夠用於回顯簡單變量,不必定是複雜變量
java

 

[neau@mail ~]$ NAME=VALUEBANK [neau@mail ~]$ VALUEBANK [neau@mail ~]$ echo $NAME VALUEBANK

 

2 執行含有字符串的命令
首先咱們首先建立一個名爲test的小文件,在這個小文件中含有一些文本。接着,將cat test賦給變量myfile,如今咱們e c h o該變量,看看是否可以執行上述命令。
linux

[neau@mail ~]$ vi test [neau@mail ~]$ cat test Hello World!!! I am a chinese Boy!

 

將cat testf賦給變量myfile
 [neau@mail ~]$ myfile="cat test" 
若是咱們e c h o該變量,咱們將沒法列出t e s t 文件中的內容。
程序員

[neau@mail ~]$ echo $myfile cat test

 

讓咱們來試一下e v a l命令,記住e v a l命令將會對該變量進行兩次掃瞄。
shell

[neau@mail ~]$  Hello World!!! I am a chinese Boy!

 

從上面的結果能夠看出,使用e v a l命令不但能夠置換該變量,還可以執行相應的命令。第
一次掃描進行了變量置換,第二次掃描執行了該字符串中所包含的命令cat test。
3 命令還能夠用來顯示出傳遞給腳本的最後一個參數
數據庫

[neau@mail ~]$ cat test1 #!/bin/bash echo "Total of the arguments passed $#"
echo "The process Id is $$"
echo "Last argument os "$( [neau@mail ~]$ ./test1 value bank test last Total of the arguments passed 4 The process Id is 21545 Last argument os last

 

在上面的腳本中, e v a l命令首先把$ $ #解析爲當前s h e l l的參數個數,而後在第二次掃描時
得出最後一個參數。
4 給每一個值一個變量名
能夠給一個值一個變量名。下面我對此作些解釋,假定有一個名爲test2的文件:
編程

[neau@mail ~]$ cat test2 CCTV 5 CHANGEL SPORTS LIKE YES

 

你但願該文件中的第一列成爲變量名,第二列成爲該變量的值,這樣就能夠:
bash

 

[neau@mail ~]$ cat test2 COMMANY TQ LANGUE ENGLISH LIKE YES [neau@mail ~]$ cat test3 #!/bin/bash while read NAME VALUE do
done <test2 echo "$COMMANY $LANGUE $LIKE" [neau@mail ~]$ ./test3 TQ ENGLISH YES

 

 

 


 

轉載自  http://www.blogjava.net/jasmine214--love/archive/2010/11/26/339106.html函數

 

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=50 50存到ptrx指向的變量中。

echo $x

50 打印50


 

功能是:建立一個mkdircd函數(加在.bashrc文件中,能夠建立一個mkdircd命令),用於建立新一個目錄並同時cd到些目錄中(不少狀況下,咱們建立一個新的目錄後,老是要進入這個目錄中,使用此命令能夠在一個命令中完成此功能)。

 

看到代碼時,不明白命令中eval的做用,後來查了一個資料,發現eval的功能以下:

語法:eval cmdLine

eval會對後面的cmdLine進行兩遍掃描,若是第一遍掃描後,cmdLine是個普通命令,則執行此命令;若是cmdLine中含有變量的間接引用,則保證間接引用的語義。

 

舉例以下:

set 11 22 33 44

若是要輸出最近一個參數,即44,可使用以下命令,

echo $4

可是若是咱們不知道有幾個參數的時候,要輸出最後一個參數,你們可能會想到使用$#來輸出最後一個參數,

若是使用命令:

echo "\$$#"

則獲得的結果是 $4,而不是咱們想要的44。這裏涉及到一個變量間接引用的問題,咱們的本意是輸出 $4,默認狀況下,命令後忽略變量間接引用的狀況。

這時候,就可使用eval命令。

eval echo "\$$#"

獲得的結果爲44


 轉載自:http://www.linuxso.com/shell/13692.html

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


一、將命令行分紅由固定元字符集分隔的記號;
SPACE, TAB, NEWLINE, ; , (, ), <, >, |, &
記號類型包括單詞,關鍵字,I/O重定向符和分號。
二、檢測每一個命令的第一個記號,查看是否爲不帶引號或反斜線的關鍵字。
若是是一個開放的關鍵字,如if和其餘控制結構起始字符串,function,{或(,則命令實際上爲一複合命令。shell在內部對複合命令進行處理,讀取下一個命令,並重復這一過程。若是關鍵字不是複合命令起始字符串(如then等一個控制結構中間出現的關鍵字),則給出語法錯誤信號。
三、依據alias.html' target='_blank'>別名列表檢查每一個命令的第一個關鍵字;
若是找到相應匹配,則替換其別名定義,並退回第一步;不然進入第4步。該策略容許遞歸別名,還容許定義關鍵字別名。如alias procedure=function
四、執行大括號擴展,例如a{b,c}變成ab ac
五、若是~位於單詞開頭,用$HOME替換~。
使用usr的主目錄替換~user。
六、對任何以符號$開頭的表達式執行參數(變量)替換;
七、對形式$(string)的表達式進行命令替換;
這裏是嵌套的命令行處理。
八、計算形式爲$((string))的算術表達式;
九、把行的參數,命令和算術替換部分再次分紅單詞,此次它使用$IFS中的字符作分割符而不是步驟1的元字符集;

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

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

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

2、關於引用

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

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

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 builtin enable

上面的命令行說起過,第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博大精深,但願你們好好學習。:)


看到另一個例子,也很實用

給每一個值一個變量名
能夠給一個值一個變量名。下面我對此作些解釋,假定有一個名爲test2的文件:

[neau@mail ~]$ cat test2 CCTV 5 CHANGEL SPORTS LIKE YES

你但願該文件中的第一列成爲變量名,第二列成爲該變量的值,這樣就能夠:

[neau@mail ~]$ cat test2 COMMANY TQ LANGUE ENGLISH LIKE YES [neau@mail ~]$ cat test3
#!/bin/bash
while read NAME VALUE
do
eval "${NAME}=${VALUE}"
done <test2
echo "$COMMANY $LANGUE $LIKE"
[neau@mail ~]$ ./test3 TQ ENGLISH YES
相關文章
相關標籤/搜索