Shell脚本之:EVAL and EXEC command

由于工作上的需要,花了点时间,研究了一下eval和exec这两个shell内建特殊的命令。因为用的不是很多,所以还是有一点比较隐晦的。。

1.eval

该命令是bashshell中内建的一个命令,相比其他的命令来说还是有一点的难度。该命令后面所跟的内容都认为是参数,但是会两次扫描其参数,第一次扫描会将参数中的变量进行替换,第二
次扫描会将后面的参数当作一个shell中的命令组合来执行命令。其常用的情况是对变量的处理
下面看几个例子:

案例1:直接组合命令
[[email protected] shellscripts]# eval ls -l /home/shellscripts/
total 8
-rwxr-xr-x 1 root root 71 Sep  9 13:29 1.sh
-rwxr-xr-x 1 root root 83 Sep  9 13:17 test.sh
[roo[email protected] shellscripts]# ls -l /home/shellscripts/
total 8
-rwxr-xr-x 1 root root 71 Sep  9 13:29 1.sh
-rwxr-xr-x 1 root root 83 Sep  9 13:17 test.sh
[[email protected] shellscripts]#
从上面的信息可以看出,当eval后面直接跟原生参数时,直接当作命令来执行,此时的执行结果,跟在shell中直接执行命令没有任何区别

案例2:替换变量
写如下一个脚步:
[[email protected] shellscripts]# cat 1.sh
#!/bin/bash
dirpath=/home/shellscripts
cmd="ls -l $dirpath | awk -F ‘ ‘ ‘{print \$9}‘"
eval $cmd
[[email protected] shellscripts]#
[[email protected] shellscripts]# ./1.sh

1.sh
test.sh
[[email protected] shellscripts]# ls -l /home/shellscripts/ | awk -F ‘ ‘ ‘{print $9}‘

1.sh
test.sh
[[email protected] shellscripts]#

从上面的信息可以看出最终的执行结果和在命令行直接执行命令的结果是一致的,那也就是说eval执行$cmd的时候,第一次先将变量$cmd用其值替换了,第二次将变量$cmd的值直接在shell中以命令的形式来执行。

注:在上面的脚本中,相信你应该能注意到\$9,为什么要加个转意字符(\),如果不加的话会被当作位置变量进行替换

案例3:可以执行任何值为命令组合的变量
先看例子:
[[email protected] shellscripts]# cat 1.sh
#!/bin/bash
dirpath=/home/shellscripts
cmd="ls -l $dirpath | awk -F ‘ ‘ ‘{print \$9}‘"
#eval $cmd     /注释掉该行
$cmd
[[email protected] shellscripts]#

上面的脚本中,注释掉了eval所在行,直接调用$cmd变量,看起来变量cmd的值是一个shell中的命令组合,似乎应该可以正常运行,好运行一下:
[[email protected] shellscripts]# ./1.sh
ls: cannot access |: No such file or directory
ls: cannot access awk: No such file or directory
ls: cannot access ‘: No such file or directory
ls: cannot access ‘: No such file or directory
ls: cannot access ‘{print: No such file or directory
ls: cannot access $9}‘: No such file or directory
/home/shellscripts:
total 8
-rwxr-xr-x 1 root root 103 Sep  9 13:52 1.sh*
-rwxr-xr-x 1 root root  83 Sep  9 13:17 test.sh*
[[email protected] shellscripts]#

额,看到没,报错了,除了识别了ls -l $dirpath的部分 ,管道符之后的内容一概为识别。但是从案例2中,可知使用eval $cmd的时候,就可以很好的执行。

从案例2和案例3的对比,可以知道,在实际使用中,可以将任意组合的命令赋值给一个变量,然后在需要的位置通过eval $variable来执行这个命令。试想如果你遇到了一个很复杂的命令组合,
可能在多个地方需要执行,此时使用eval的功能就很方便。当然,有人可能会说可以使用函数,但是函数只有在调用的时候才会生效,且每次调用都要到内存中执行一遍,变量一旦赋值,直到变量的生命周期结束都会存在内层中供其调用。

案例4:变量替换赋值
看例子:
创建一个脚步如下:
[[email protected] shellscripts]# cat 2.sh
#!/bin/bash
x=100
y=x
eval echo \$$y
eval $y=50
echo $x
eval echo \$$y
[[email protected] shellscripts]#

执行脚步看结果:
[[email protected] shellscripts]# ./2.sh
100
50
50
[[email protected] shellscripts]#

分析:从上面的结果看,脚步返回了三个值。脚本运行时,先对x变量进行赋值,然后对y变量进行赋值,接着执行eval echo \$$y,返回了一个100,到这里还可以理解。接着执行eval $y=50,然
后尝试着输出$x的值,发现输出了一个50。一开始x变量的值是100,这里却变成了50 why?原因就在eval $y=50这句话,这句话相当于一个变量赋值,$y替换成x而将50赋值给了x变量,因此x
变成了50,而最后一句话再次执行eval \$$y验证其结果,发现确实变成了50。。。

2.exec
    exec也是shell内建的一个命令。类似与eval,source,但是不同的是exec执行后面的命令后会替换当前的shell进程,而前两者不会。。关于exec的用法确实有点特殊,可能编程的人士会对
其比较熟悉,这里介绍三种比较常见的用法;

用法一:用来分离执行脚本,并退出子脚本的shell进程
主脚本:
[[email protected] shellscripts]# cat main.sh
#!/bin/bash
bash /home/shellscripts/3.sh
ls -al
[[email protected] shellscripts]#

子脚本:
[[email protected] shellscripts]# cat 3.sh
#!/bin/bash
ls -l /etc | head -n 5
exec echo "this is a test"
echo "hello world"
[[email protected] shellscripts]#

执行主脚本,看结果:
[[email protected] shellscripts]# ./main.sh
total 1252
-rw-r--r--.  1 root root     44 Sep  5 18:13 adjtime
-rw-r--r--.  1 root root   1512 Jan 12  2010 aliases
-rw-r--r--.  1 root root  12288 Sep  2 09:24 aliases.db
drwxr-xr-x.  2 root root   4096 Sep  2 11:57 alternatives
this is a test
total 44
drwxr-xr-x  2 root root 4096 Sep  9 15:51 .
drwxr-xr-x. 3 root root 4096 Sep  9 14:19 ..
-rwxr-xr-x  1 root root  103 Sep  9 13:52 1.sh
-rwxr-xr-x  1 root root   71 Sep  9 14:02 2.sh
-rwxr-xr-x  1 root root   81 Sep  9 15:51 3.sh
-rwxr-xr-x  1 root root   92 Sep  9 15:43 4.sh
-rwxr-xr-x  1 root root   31 Sep  9 15:16 5.sh
-rwxr-xr-x  1 root root   72 Sep  9 15:43 6.sh
-rw-r--r--  1 root root   41 Sep  9 15:12 file
-rwxr-xr-x  1 root root   49 Sep  9 14:32 main.sh
-rwxr-xr-x  1 root root   83 Sep  9 13:17 test.sh
[[email protected] shellscripts]#

分析:
    从上面的脚本输出信息可以看到成功调用了3.sh脚本,但是你会发现3.sh脚本中的最后一行没有执行,因为没有输出内容。这是因为在输出了exec后面的命令执行结果后,就清除了3.sh脚步
打开的子shell进程,而后回到主shell进程继续执行后面的内容。。

这个特性似乎看起来没多大用处,有人说直接写在一个脚步里好了。但是对于复杂的环境,避免干扰,或许你需要这样的用法。。

用法二:用来设置描述符重定向输入文件内容
脚本如下:
[[email protected] shellscripts]# cat 4.sh
#!/bin/bash
exec 3</home/shellscripts/file
while read -u 3 str
do
echo $str
done
exec 3<&-

创建/home/shellscripts/file文件:
[[email protected] shellscripts]# cat file
hello world haha a b c
liuxueming
liwei
[[email protected] shellscripts]#

执行脚本,看结果:
[[email protected] shellscripts]# ./4.sh
hello world haha a b c
liuxueming
liwei
[[email protected] shellscripts]#

从上面的执行结果看,正常的输出了文本的内容,且按照while循环的次序输出。这里要解释的是文件描述符。exec可以实现文件描述符的重定向,将某个文件描述符关联到某个文件,以后对此
文件描述符的操作就是对该文件的操作,这也符合linux系统文件描述符的定义。例如上面的脚步将3重定向输入到/home/shellscripts/file文件,这样在下面就可以使用read -u 3来指定该描述
符,否则read后面不可以直接添加文件。当调用完成后,还要关闭文件描述符,例如上面的exec 3<&-

当然这里还可以使用另外一种方法:
while read str
do
echo $str
done < /home/shellscripts/file

用法三:用来设置描述符重定向输出内容至文件
看实验:
[[email protected] fd]# pwd
/dev/fd
[[email protected] fd]# ls
0  1  2  255
[[email protected] fd]# exec 4>/home/shellscripts/test
[[email protected] fd]# ls -l /etc | head -n 5 > 4
[[email protected] fd]# exec 4>&-
[[email protected] fd]# cat /home/shellscripts/test
total 1252
-rw-r--r--.  1 root root     44 Sep  5 18:13 adjtime
-rw-r--r--.  1 root root   1512 Jan 12  2010 aliases
-rw-r--r--.  1 root root  12288 Sep  2 09:24 aliases.db
drwxr-xr-x.  2 root root   4096 Sep  2 11:57 alternatives

看到没,重定向输出到文件描述符4的内容,都被输出到文件:/home/shellscripts/test中了。。。

结束!!!

笨蛋的技术------不怕你不会!!!

时间: 2024-10-17 08:07:00

Shell脚本之:EVAL and EXEC command的相关文章

linux下shell脚本执行方法及exec和source命令

exec和source都属于bash内部命令(builtins commands),在bash下输入man exec或man source可以查看所有的内部命令信息. bash shell的命令分为两类:外部命令和内部命令.外部命令是通过系统调用或独立的程序实现的,如sed.awk等等.内部命令是由特殊的文件格式(.def)所实现,如cd.history.exec等等. 在说明exe和source的区别之前,先说明一下fork的概念. fork是linux的系统调用,用来创建子进程(child

shell脚本运行报错$&#39;\r&#39;: command not found

执行个别shell测试脚本运行报错$'\r': command not found 考虑到可能是windows与Linux的换行符不同的原因(windows是\r\n,Linux是\n)造成的,但是又不想一个个替换. 可以使用如下命令来解决: # vi 脚本名 命令行模式下输入: :set ff=unix :wq 退出即可 shell脚本运行报错$'\r': command not found 原文地址:https://www.cnblogs.com/abclife/p/12604441.htm

shell脚本在cygwin下运行报错: $&#39;\r&#39;: command not found

在cygwin 下运行shell脚本,出现"$'\r': command not found",这是win dos与Unix文本编辑方式不同造成的.可以使用cygwin工具dos2unix将script改为unix格式. $ dos2unix test.sh dos2unix: converting file test.sh to UNIX format ... $ sh test.sh 如此便可解决. 参见:http://lxs647.iteye.com/blog/2084375 s

执行shell脚本报错 &#39;\357\273\277&#39;: command not found 解决办法

1,删除BOM,在vi下面执行下面的命令即可 :set nobomb 2,原因: 所谓BOM,全称是Byte Order Mark,它是一个Unicode字符,通常出现在文本的开头,用来标识字节序(Big/Little Endian),除此以外还可以标识编码(UTF-8/16/32) 对于UTF-8/16/32而言,它们名字中的8/16/32指的是编码单位是多少位的,也就是说,它们的编码单位分别是8/16/32位,换算成字节就是1/2/4字节,如果是多字节,就要牵扯到字节序,UTF-8以单字节为

shell脚本中的eval

eval的作用是再次执行命令行处理,也就是说,对一个命令行,执行两次命令行处理. 1.例子1:用eval技巧实现shell的控制结构for 用eval技巧实现shell的控制结构for. [[email protected] root]# cat myscript1#!/bin/shevalit(){       if [ $cnt = 1 ];then               eval [email protected]               return       else   

Linux shell脚本中调用另一个shell(exec、source、fork)

  在运行shell脚本时候,有三种方式来调用外部的脚本,exec(exec script.sh).source(source script.sh).fork(./script.sh) exec(exec /home/script.sh): 使用exec来调用脚本相当于在当前shell执行了一条命令,不会产生新的进程,被执行的脚本会继承当前shell的环境变量.但是当exec调用完毕后,当前shell也会结束,剩下的代码不会执行. source(source /home/script.sh) 使

转 MySQL shell脚本执行错误 $&#39;\r&#39;:command not found

问题描述 前几天编写的shell小脚本,测试自动安装MySQL的,今天测试运行,然后出现如下错误$’\r’:command not found, 问题分析 检查脚本,没有问题,只是有空行.提示这个错误也不是代码本身错误,怀疑可能是编码格式等错误,:脚本是Linux下编辑完成测试.后期我又做了简单修改,是在在window下修改后完成,然后上传到Linux服务器的.注意到:win下的换行是回车符+换行符,也就是\r\n,而unix下是换行符\n. linux下不识别\r为回车符,如果脚本有\r回车符

Linux-006-执行Shell脚本报错 $&#39;\r&#39;:command not found

在 windows 下编写 Shell 脚本,在 Linux 上执行时,报错提示: $'\r':command not found. 因为 windows 下的换行符是 \r\n ,而 Linux 的换行符是 \n.因而在 Linux 下运行 windows 编写的 Shell 脚本,会报如上所示的错误. 解决方法:将换行符替换成 Linux 平台的换行符即可.命令如下所示: sed -i 's/\r//' 脚本名 Linux-006-执行Shell脚本报错 $'\r':command not

shell脚本交互:expect学习笔记及实例详解

最近项目需求,需要写一些shell脚本交互,管道不够用时,expect可以很好的实现脚本之间交互,搜索资料,发现网上好多文章都是转载的,觉得这篇文章还不错,所以简单修改之后拿过来和大家分享一下~ 1. expect是spawn: 后面加上需要执行的shell命令,比如说spawn sudo touch testfile 1.3 expect: 只有spawn执行的命令结果才会被expect捕捉到,因为spawn会启动一个进程,只有这个进程的相关信息才会被捕捉到,主要包括:标准输入的提示信息,Li