功能说明:重新运算求出参数的内容。. {) y0 Q4 \$ H- g! [ S7 [+ ~
7 c& y5 ]) e# H0 W, o' [语 法:eval [参数]
5 o2 E& m8 ?0 d: J$ }, [$ F
+ M7 r( t; G V; X: a补充说明:eval可读取一连串的参数,然后再依参数本身的特性来执行。
* L: r/ m. @, s9 Q5 W0 W% ^% l' Z( U& e1 E" h0 R$ v0 ^. G
参 数:参数不限数目,彼此之间用分号分开。
K3 R0 [9 E3 N9 Q, U1 {1.eval命令将会首先扫描命令行进行所有的替换,憨厚再执行命令。该命令使用于那些一次扫描无法实现其功能的变量。该命令对变量进行两次扫描。这些需要进行两次扫描的变量有时候被称为复杂变量。 2.eval也可以用于回显简单变量,不一定时复杂变量。 NAME=ZONE eval echo $NAME等价于echo $NAME 3.两次扫描 test.txt内容:hello shell world! (1)echo $myfile #result:cat test.txt (2)eval echo $myfile #result:hello shell world! 从(2)可以知道第一次扫描进行了变量替换,第二次扫描执行了该字符串中所包含的命令 4.获得最后一个参数 echo "Last argument is $(eval echo \$$#)" echo "Last argument is $(eval echo $#)"
, g' i+ I4 b; k. G: Gshell 也提供了 eval 命令,如同熟悉的其他脚本语言,会将它的参数做为命令执行,初看会疑惑为什么shell要提供两种动态执行命令字串的机制,但是经过仔细分析,才发现shell的eval同其他语言有很大区别。
; f" J5 m2 E7 Y! F7 I5 r3 i- p. T6 Z9 \- a
1.shell 中的函数虽然可以通过return 返回,但是这里的return 相当于 exit,只能是个状态值用于测试,而不能像其它语言一样返回复杂的结果,其处理结果只能通过输出到标准输出经过 `` ,$()取得。! a+ t1 `; T& j% V3 q
3 D) R* d/ m5 u g( Y
2.shell 中的 eval4 [1 H& H% t" Q
) B1 |$ v M- z# n k$ V
2.1 不能获得函数处理结果 ,如1所说,所有命令,函数的处理结果只能通过 ``来获得,那么其它语言中利用eval来获得动态生成代码执行后的输出变得不可能。3 z/ K8 G1 D) S% A: u2 z- d
: O! y1 S) [* U% Z2 P3 A 2.2 eval 嵌套无意义 ,在其他语言中可以通过 eval(eval("code")),来执行(执行动态生成的code的返回),而由于shell 中 eval 将后面的eval命令简单当作命令字符串执行,失去了嵌套作用,嵌套被命令替换取代。 扩展阅读:eval命令使用示例详解 资料整理www.linuxso.com eval的作用是再次执行命令行处理,也就是说,对一个命令行,执行两次命令行处理。这个命令要用好,就要费一定的功夫。我举两个例子,抛砖引玉。
7 {; L e1 [ R& i
5 u& l- F9 n% E9 g3 a) Q! ?2 L1、例子1:用eval技巧实现shell的控制结构for
% `/ l1 ?% N/ f0 e4 P* g
" E# Y: a: r1 J3 a" T K用eval技巧实现shell的控制结构for。6 O8 c1 q, _8 o0 E4 ]' k$ M" v
. X) |2 f$ `/ k) Z7 ]- p. K[root@home root]# cat myscript1
: h* y! \/ Q' a+ D7 R& F) p/ i0 _- u1 Y1 d3 J
QUOTE: L( y- y; c4 ?1 Q
#!/bin/sh9 N& S* S7 k' z$ w% A
evalit(){
; B, a, L4 K, k8 E if [ $cnt = 1 ];then
0 S/ L, M! n' ]+ a% u eval $@6 G7 N* [& y; e& Z# r) l5 j
return) S9 z- M @* g, d, `
else
0 U: }" f( T2 F" T$ E8 L8 l let cnt="cnt-1"6 n1 S& K0 b7 S& L
evalit $@
; Y' h' m/ o0 J7 { fi1 E- x5 c0 o9 B& c5 C$ {: L3 m' |
eval $@
. @; E3 f8 B4 E N' T}
. P- b6 C' q& v2 ^. a5 zcnt=$1* ]7 `% E! F& a
echo $cnt | egrep "^[1-9][0-9]*$" >/dev/null
; z5 ^! _. z! k6 p# t: }: w# X# j& cif [ $? -eq 0 ]; then, J6 ]2 I& B4 V& `; R
shift; j* x; g t- i
evalit $@$ o& X. n: S* x
else
2 F1 K( c. p) h) L echo 'ERROR!!! Check your input!'3 s7 [3 {" l, c3 N& ~5 @3 g! m
fi/ }. F$ H2 c! G, J' r' ~8 k3 g
[root@home root]# ./myscript1 3 hostname6 D* ?+ ^! I6 {. ~% [
home7 X/ v& V2 d/ i3 |$ p5 x
home1 |3 A# a4 p7 G8 J
home
9 R7 j" O! j6 h/ Z9 I% K[root@home root]# ./myscript1 5 id |cut -f1 -d' '& h% B# T/ X2 | y& Q5 j- J
uid=0(root)0 L7 L+ O0 E7 H! m6 v5 g
uid=0(root)
; N s9 x# M3 Tuid=0(root)
$ A8 i& F. V& n- n, ^* J" }uid=0(root)" x. ]/ t7 S! ~% v( r& `; h" c
uid=0(root)+ l" e2 m' R3 ?1 n9 _
注意:bash里有两个很特殊的变量,它们保存了参数列表。
) U& D/ S. Y4 T5 p( |: @: l7 C5 h8 c0 c) Q, M! J) R9 S
$*,保存了以$IFS指定的分割符所分割的字符串组。
6 W0 J" q# m2 P" G8 f& u: ~+ s$@,原样保存了参数列表,也就是"$1""$2"...# I+ F# ]% q) i, a' f5 u
: O+ ~# T( P* w这里我使用了函数递归以及eval实现了for结构。 J3 U* y: S8 q4 T$ e
当执行eval $@时,它经历了步骤如下:
2 \& ~7 |0 G0 P) Y& {8 W第1步,分割成eval $@; B" V% P- O: P Q9 N7 {
第6步,扩展$@为hostname) u4 b2 j6 F0 E
第11步,找到内置命令eval2 y$ R7 Y. c% L8 X a- Q) V
重复一次命令行处理,第11步,找到hostname命令,执行。
: L0 y6 [* D3 G# |8 r0 M) p+ O
O2 G7 y2 N3 V: J5 W8 h注意:也许有人想当然地认为,何必用eval呢?直接$@来执行命令就可以了嘛。
+ s8 J( T/ N3 t4 [ }; }) a9 b ~- J: v% A# U$ m
例子2:一个典型错误的例子
$ m3 M; w% b+ H y
( \5 b1 A4 s! a& v错误!这里给个典型的例子大家看看。7 ?" w3 e+ _9 J$ F+ ~: {0 x# S
! _. D. J$ Z: O: S: O2 P
3 y4 N+ C9 V3 A! L( j[root@home root]# a="id | cut -f1 -d' '"% i$ ^- s: k7 m u' u
[root@home root]# $a# H6 n! `/ j% N3 P- c
id:无效选项 # f
% Q4 r% a4 U$ r5 ^请尝试执行‘id # help’来获取更多信息。
7 R( k' K: R* N: B[root@home root]# eval $a. Q$ ]* @+ ]3 J$ y2 S
uid=0(root)$ ^$ u, y* f S5 e
t' `. L% N* D) `. F2 b+ J
如果命令行复杂的话(包括管道或者其他字符),直接执行$a字符串的内容就会出错。分析如下。
+ a5 @, y% M6 |/ G0 s$a的处理位于第6步──参数扩展,也就是说,跳过了管道分析,于是"|", "cut", "-f1", "-d"都变成了id命令的参数,当然就出错啦。 \( m3 P. y( e3 x5 {
但使用了eval,它把第一遍命令行处理所得的"id", "|", "cut", "-f1", "-d"这些字符串再次进行命令行处理,这次就能正确分析其中的管道了。
+ M q9 |5 W$ P9 t ?( U& A- z, m6 t! e! ?4 b0 ~' z2 S* X+ o
总而言之:要保证你的命令或脚本设计能正确通过命令行处理,跳过任意一步,都可能造成意料外的错误!5 m. E$ e% p' u
* X F" r. J4 U3 R5 V
例子3:设置系统的ls色彩显示4 I) b! ~ d* O& n2 y1 z5 i" k
3 @. ^( v- v6 B8 E2 b; x8 h! C9 d4 a
3 S3 _' G- n) `; Geval $(dircolors -b /etc/dircolors)4 y- m7 e0 w* ~" Q! j
2 J+ V* r! o4 K% [- _
eval语句通知shell接受eval参数,并再次通过命令行处理的所有步骤运行它们。
a+ J* Z' V; B它使你可以编写脚本随意创建命令字符串,然后把它们传递给shell执行;
9 g0 ^% R/ \* L/ t) r$()是命令替换,返回命令的输出字符串。
" K6 t" S5 V0 @其中dircolors命令根据/etc/dircolors配置文件生成设置环境变量LS_COLORS的bash代码,内容如下% P" @. E" d6 p: J: o0 K1 R9 ^* ~
$ E5 s. p X ]0 ?
[root@localhost root]# dircolors -b > tmp: X0 O9 t8 Q2 W$ b. _1 L" Q4 V! T& @
[root@localhost root]# cat tmp
. }' c+ h! z# e' \' {% aLS_COLORS='no=00:fi=00:di=01;34:ln=01; ......
% |* N1 A' F4 h' f; L4 nexport LS_COLORS
0 ~ ?9 p5 V i' V$ b( \#这里我没有指定配置文件,所以dircolors按预置数据库生成代码。7 Z4 T! z5 ?4 o/ z
其输出被eval命令传递给shell执行。
' l# V, U1 C) p4 \; i7 h. K4 d* p* X" {4 r2 n
eval是对Bash Shell命令行处理规则的灵活应用,进而构造"智能"命令实现复杂的功能。- r$ f2 X! |8 g9 o: G8 ~9 r- q5 o
上面提及的命令是eval其中一个很普通的应用,它重复了1次命令行参数传递过程,纯粹地执行命令的命令。
0 J1 e# P2 m1 h; H6 q其实它是bash的难点,是高级bash程序员的必修之技。
% P4 g( u9 ]6 \1 l6 | |