功能说明:重新运算求出参数的内容。
& c3 Q$ H6 W( m# G/ c6 j
6 B) {7 E+ Y+ u* q6 n语 法:eval [参数]8 o7 [ V8 [* C6 ~, ]; L
" d* E. g7 b8 _* v# A" S$ ~% r补充说明:eval可读取一连串的参数,然后再依参数本身的特性来执行。0 u' q- f4 N6 v. e$ f, O. R
" n+ `. g, Y8 Y0 \参 数:参数不限数目,彼此之间用分号分开。 $ n) s& y, t9 E6 ?- s3 H
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 $#)" ( W& t3 U4 J$ n5 B# e" M
shell 也提供了 eval 命令,如同熟悉的其他脚本语言,会将它的参数做为命令执行,初看会疑惑为什么shell要提供两种动态执行命令字串的机制,但是经过仔细分析,才发现shell的eval同其他语言有很大区别。* D/ x7 D5 D4 S; c- F" p
0 C7 x8 i0 ^6 ~) } c1.shell 中的函数虽然可以通过return 返回,但是这里的return 相当于 exit,只能是个状态值用于测试,而不能像其它语言一样返回复杂的结果,其处理结果只能通过输出到标准输出经过 `` ,$()取得。
: D, v4 A1 y+ V" ~) f! y. H1 q( T( }9 E
2.shell 中的 eval+ O' J' e/ X, }1 g3 B! L) V9 v
& z1 n5 Z' ~6 w 2.1 不能获得函数处理结果 ,如1所说,所有命令,函数的处理结果只能通过 ``来获得,那么其它语言中利用eval来获得动态生成代码执行后的输出变得不可能。
3 p% G1 S* s" e7 Y5 T6 [+ X) _+ p" V
2.2 eval 嵌套无意义 ,在其他语言中可以通过 eval(eval("code")),来执行(执行动态生成的code的返回),而由于shell 中 eval 将后面的eval命令简单当作命令字符串执行,失去了嵌套作用,嵌套被命令替换取代。 扩展阅读:eval命令使用示例详解 资料整理www.linuxso.com eval的作用是再次执行命令行处理,也就是说,对一个命令行,执行两次命令行处理。这个命令要用好,就要费一定的功夫。我举两个例子,抛砖引玉。
5 _& R& a' j' h& E8 [& \& Z
- K1 `5 E, l2 [8 D8 r1、例子1:用eval技巧实现shell的控制结构for
! Z1 V. |' w3 Z5 ]: ~
7 F- P, H0 v' P4 T2 k' U9 m# i用eval技巧实现shell的控制结构for。" L. x6 g( H) x2 ?5 T E) }, V
; H$ i1 n- e0 T' b) r. w" q( d1 a[root@home root]# cat myscript1
( u* N3 U# {+ R6 V/ \0 F, {2 D G/ a# q
QUOTE:
" d7 `. A, }% ?#!/bin/sh# h8 }) J$ z# ~4 h
evalit(){
- m$ V9 w% x; d1 J2 x* T# Z if [ $cnt = 1 ];then9 F$ R8 \7 x! v
eval $@
8 m( ^0 M, _% c0 i return# X; C7 E2 Z! j6 B. x' R) R
else5 t% z4 k5 d3 U& R. \; h8 ~" |
let cnt="cnt-1"
# Y O" F& y5 v/ U! u+ A# T: X4 y evalit $@
7 B! v: Z0 i4 l" J7 n% k fi
6 j$ e# l! \' V eval $@
/ C! W& i1 F! D4 |}0 s" I4 J3 H9 ]
cnt=$13 ~! T* [- f2 f' x) K
echo $cnt | egrep "^[1-9][0-9]*$" >/dev/null$ e2 z% J; d Q1 W* d! W; _* U
if [ $? -eq 0 ]; then, e: a) _; v- {4 Z
shift
: g6 n4 w1 z: h$ U2 U% F# | evalit $@
( V3 n* P+ c- P4 u5 [; gelse
8 D! J* J$ ?( h1 l echo 'ERROR!!! Check your input!'
$ s( W X- A* \# G: K& o7 V0 Qfi
$ A6 x+ S! M+ p! Y# @[root@home root]# ./myscript1 3 hostname1 O. }) W8 F, T) m) c( H
home
- e2 c- d3 _* h6 @3 y% _home4 @6 v* _$ L0 T8 G- a/ u4 T
home$ S8 _' O! T6 I$ d7 [
[root@home root]# ./myscript1 5 id |cut -f1 -d' '' q, M5 A6 s; @ X5 ~ O
uid=0(root)
9 l8 O- ]2 l8 u) q. Duid=0(root)
6 I. E' f: K6 t: H3 E$ }uid=0(root)& v% ^$ {$ g5 X! v2 m4 J$ n
uid=0(root)" T4 {. @3 ?% E) P0 h3 @3 y3 y7 |
uid=0(root)
+ ^. B8 e/ Q! a0 R* [# c注意:bash里有两个很特殊的变量,它们保存了参数列表。
& f4 F" S) u/ y. Q
, }4 Y1 n- e; i0 `: q4 Q3 u$*,保存了以$IFS指定的分割符所分割的字符串组。
$ p2 c% [8 V+ |) v; ?$@,原样保存了参数列表,也就是"$1""$2"...
9 Y" \1 D$ F8 F
. ]* k0 l5 h+ M2 q0 J这里我使用了函数递归以及eval实现了for结构。+ W/ a5 L4 P) L
当执行eval $@时,它经历了步骤如下:
$ R% n, ?! J- p+ o3 z4 C9 B) c第1步,分割成eval $@
. [( I$ c/ K+ w& g第6步,扩展$@为hostname4 y( s$ S4 S9 M: Y: M- j
第11步,找到内置命令eval/ J" N9 b3 d8 W" [* }. J( H
重复一次命令行处理,第11步,找到hostname命令,执行。+ b, ^9 m' R b3 L! O( G3 F% M
" ?( ?% |$ A& v) l
注意:也许有人想当然地认为,何必用eval呢?直接$@来执行命令就可以了嘛。
& L/ O2 J; \* J3 U7 J/ R
- e' v0 g- X# M6 J% u! k* }例子2:一个典型错误的例子' P9 D: i4 R+ b$ z
1 W" w& Z7 k$ O! o错误!这里给个典型的例子大家看看。6 s! J( H3 y) H5 H. h; Y: I
/ A, j) ?* ~) t. V0 a+ e
: N2 g, ?! E# b- l
[root@home root]# a="id | cut -f1 -d' '"
% C1 o; j, ?; @% ][root@home root]# $a
; V4 Z) S6 a2 G3 x+ U! n0 U7 ~id:无效选项 # f
6 i/ W* m: Y" l0 d, [- T5 X请尝试执行‘id # help’来获取更多信息。6 i7 _% x" `0 B+ H n
[root@home root]# eval $a' O+ _( a6 `/ X+ o' {/ j7 ?# {' H
uid=0(root)+ h' s7 ~: M8 o, ~ q C- I
5 a. Y, C3 D2 C9 a+ E如果命令行复杂的话(包括管道或者其他字符),直接执行$a字符串的内容就会出错。分析如下。
1 R) D# |: ?& t$a的处理位于第6步──参数扩展,也就是说,跳过了管道分析,于是"|", "cut", "-f1", "-d"都变成了id命令的参数,当然就出错啦。& N) k4 U4 `4 M8 U- d9 @' e
但使用了eval,它把第一遍命令行处理所得的"id", "|", "cut", "-f1", "-d"这些字符串再次进行命令行处理,这次就能正确分析其中的管道了。
/ C0 _$ R2 m! s6 N5 }/ y7 w- w. z2 P8 P- e
总而言之:要保证你的命令或脚本设计能正确通过命令行处理,跳过任意一步,都可能造成意料外的错误!7 T7 \2 M/ Z9 r- ]
8 X9 u# z$ T `. v* ]! y* P例子3:设置系统的ls色彩显示" q! b7 G& L8 A' G
1 {, [& A1 L q/ R, y; c& }. G6 O: u+ @* y3 G+ q
eval $(dircolors -b /etc/dircolors)
5 z6 u+ g) a4 P4 E, O( y& h
) n. ] m9 S" c/ d* _eval语句通知shell接受eval参数,并再次通过命令行处理的所有步骤运行它们。
6 r6 v' t4 @: Z d. I |4 v2 s% T( B它使你可以编写脚本随意创建命令字符串,然后把它们传递给shell执行;4 ^; P5 i& @* x8 |+ }7 ]
$()是命令替换,返回命令的输出字符串。
7 Y' H) g1 W. ]/ m: g6 t其中dircolors命令根据/etc/dircolors配置文件生成设置环境变量LS_COLORS的bash代码,内容如下
) z M4 O0 h9 O, _8 { p0 D5 f; a1 `9 C- g4 Y2 F
[root@localhost root]# dircolors -b > tmp
Q N' _; @1 d% {$ [[root@localhost root]# cat tmp
5 A0 X% N) D% o/ QLS_COLORS='no=00:fi=00:di=01;34:ln=01; ......! U- [( u5 R$ N k2 i1 A3 U
export LS_COLORS% |0 [6 o9 Q4 p& e! U
#这里我没有指定配置文件,所以dircolors按预置数据库生成代码。
$ ^1 q- P9 L. u其输出被eval命令传递给shell执行。
+ W5 {" E( E5 b+ o
$ U% P4 w$ S& e3 |: S3 b l" c" H! geval是对Bash Shell命令行处理规则的灵活应用,进而构造"智能"命令实现复杂的功能。7 l$ t" A9 G* ^
上面提及的命令是eval其中一个很普通的应用,它重复了1次命令行参数传递过程,纯粹地执行命令的命令。
R" m! L2 ^5 k# y; }其实它是bash的难点,是高级bash程序员的必修之技。 * J) z# j; k* ~2 B
|