shell学习三十八天----执行顺序和eval

执行顺序和eval

shell从标准输入或脚本中读取的每一行称为管道,它包含了一个或多个命令,这些命令被一个或多个管道字符(|)隔开.

事实上嗨哟很多特殊符号可用来分割单个的命令:分号(;),管道(|),&,逻辑AND(&&),逻辑OR(||).对于每一个地区的管道,shell都会将命令分割,为管道设置I/O,并且对每一个命令依次执行下面的操作.

看起来很复杂,但是每一个步骤都是在shell的内存里发生的,shell不会真的把每个步骤的发生演示给我们看.所以这是我们分析shell内存的情况,从而知道每个阶段的命令行是如何被转换的.

案例:

mkdir /tmp/x #建立临时性目录

cd /tmp/x #切换到该目录

touch f1 f2 #建立文件

f=f y=”a b” #赋值两个变量

echo ~+/${f}[12] $y $(echo cmd subst) $((3+2))>out      #忙碌的命令

上述命令的执行步骤:

1.命令一开始会根据shell语法而分割为token.最重要的一点是:I/O重定向>out在这里是被识别的,并存储供稍后使用.流程继续处理下面这行,其中每个token的范围显示于命令下方的行上:

echo ~+/${f}[12] $y $(echo cmd subst) $((3 + 2))

| 1 | |----- 2 ----| |3 | |-------- 4----------| |----5-----|

2.坚持第一个单词(echo)是否为关键字,例如if或for.在这里不是,所以命令行不变继续执行.

3.坚持第一个单词(依然是echo)是否为别名.这里不是,所以命令行不变继续处理.

4.扫描所有单词是否需要波浪号展开.在这里~+等同于$PWD,也就是当前目录.token2将被修改,处理继续如下:

echo /tmp/x/${f}[12] $y $(echo cmd subst) $((3 + 2))

| 1 | |------- 2 -------| |3 | |-------- 4----------| |----5-----|

5.变量展开:token2与3都被修改.产生:

echo /tmp/x/${f}[12] a b $(echo cmd subst) $((3 + 2))

| 1 | |------- 2 -------| | 3 | |-------- 4----------| |----5-----|

6.处理命令替换.注意,这里可递归引用列表里的所有步骤!在此例中,因为我们要试图让所有的东西容易理解,因此命令修改了token4,结果:

echo /tmp/x/${f}[12] a b cmd subst $((3 + 2))

| 1 | |------- 2 -------| | 3 | |--- 4 ----| |----5-----|

7.执行算术替换 .修改token5,结果:

echo /tmp/x/${f}[12] a b cmd subst 5

| 1 | |------- 2 -------| | 3 | |--- 4 ----| |5|

8.前面所有的展开产生的结果,都将再一次被扫描,看看是否有$IFS字符.如果有,则他们是作为分隔符,产生额外的单词.例如,两个字符$y原来是组成一个单词,但展开式”a空格b”,在此阶段被切分为两个单词:a与b.相同方式也应用于命令替换$(echo cmd subst)的结果上.先前的token3变成了token3与4.先前的token4则成了token5与6.结果:

echo /tmp/x/${f}[12] a b cmd subst 5

| 1 | |------- 2 -------| 3 4 |-5-| |- 6 -| 7

9.通配符展开.token2变成了token2与token3:

echo /tmp/x/$f1 /tmp/x/$f2 a b cmd subst 5

| 1 | |---- 2 ----| |---- 3 ----| 4 5 |-6-| |- 7 -| 8

10.这时,shell已经准备好了要执行最后的命令了,他会去寻找echo.正好ksh93与bash的echo都内建到shell中了

11.shell实际执行命令.首先执行>out的I/O重定向,在调用内部的echo版本,显示最后的参数.

最后的结果:

$cat out

/tmp/x/f1 /tmp/x/f2 a b cmd subst 5

eval语句

shell中的eval这个命令很神奇,他能把字符串当做命令来执行.PS:这个字符串必须是可执行的bash命令才可以.

案例:

eval “ls” #输出当前目录的所有文件

语法: eval [参数]

补充说明:eval可读取一连串的参数,然后再依惨呼本身的特性来执行.

参数:不限数目,彼此之间用分号隔开.

案例:我有一个文件test.txt

命令:cat test.txt

输出:hello world

命令:myfile="cat test.txt"

命令:echo $myfile

输出:cat test.txt

命令:eval $myfile

输出:hello world

从eval $myfile这条命令可以看出,eval进行了变量替换,将字符串中属于bash的命令执行了.

把拼接起来的字符串当作命令执行,这就是eval的神奇之处.

subShell与代码块

subShell是一群被括在圆括号里的命令,这些命令会在另外的进程中执行.当你需要让一小组的命令在不同的目录下执行时,这种方式可以让你不必修改主脚本的目录,直接处理这种情况.

例如:tar -cf -.| (cd /tmp;tar -xpf -)

左边的tar命令会产生当前目录的tar打包文件,将他传送给标准输出.这份打包文件会通过管道传递给走遍的subShell里的命令.开头的cd命令会先切换到新目录,也就是让大宝文件在此目录下解开.然后,走遍的tar将从打包文件中解开文件.注意,执行此管道的shell(或脚本)并未更改他的目录.

代码块概念上与subShell雷同,只不过他不会建立新的进程.代码块里的命令以花括号({})括起来,且对主脚本的状态会造成影响(例如他的当前目录).一般来说,花括号被视为shell关键字,意即他们只有出现在命令的第一个符号时会被识别.实际上:这表示你必须将结束花括号放置在换行字符或分号之后.例如:

cd /home/directory||{

echo could not change to /home/directory!>&2

echo you lose !>&2

exit1

}

IO重定向也可以套用subShell与代码块里.在该情况下,所有的命令会从重定向来源读取它们的输入或传送他们的输出.


subShell与代码块


结构


定界符


认可的位置


另外的进程


SubShell


()


行上的任何位置



代码块


{}


在换行字符,分号或关键字之后


注意:代码块里的exit会终止整个脚本.

我们通常在shell中运行一个脚本只需要简单的调用./[script_name]即可,这种方式下,shell会启动一个子进程来运行该脚本,称为subShell,当subShell运行完成,子进程结束.父进程的环境不会有任何改变.

案例:bash代码

#!/bin/bash

cd /var/cache

testname="fine"

分别在shell中运行

1.  ./test.sh;echo $testname  会发现还是位于原来的目录中,$testname的值书粗话为null.

2. source ./test.sh;echo $testname这里就不一样了,现在你位于/var/cache中,$testname的值也变成了fine

用source命令来运行脚本,不会产生子进程,脚本在shell的进程空间中执行,所以运行重定义的变量,执行的操作,都会在shell的运行环境中保留下来.

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-10-13 19:27:38

shell学习三十八天----执行顺序和eval的相关文章

.net学习之母版页执行顺序、jsonp跨域请求原理、IsPostBack原理、服务器端控件按钮Button点击时的过程、缓存、IHttpModule 过滤器

1.WebForm使用母版页后执行的顺序是先执行子页面中的Page_Load,再执行母版页中的Page_Load,请求是先生成母版页的控件树,然后将子页面生成的控件树填充到母版页中,最后输出 2.WebForm使用母版页进行登录时候的验证 //新建一个页面父类 public abstract class BasePage:Page { protected void Page_Load(object sender, EventArgs e) { //1.身份验证的方法 Session if (Se

JMeter学习3 - 元件执行顺序

1.元件的作用域 JMeter中共有8类可被执行的元件(测试计划与线程组不属于元件),这些元件中,取样器是典型的不与其它元件发生交互作用的元件,逻辑控制器只对其子节点的取样器有效,而其它元件(config elements .timers .post-processors.assertions.listeners.)需要与取样器(sampler)等元件交互. 配置元件(config elements ) 元件会影响其作用范围内的所有元件. 前置处理程序(Per-processors) 元件在其作

Hibernate学习之hibernate执行顺序

Hibernate 执行的顺序如下:  (1) 生成一个事务的对象,并标记当前的 Session 处于事务状态(注:此时并未启动数据库级事务).  (2) 应用使用 s.save 保存对象,这个时候 Session 将这个对象放入 entityEntries ,用来标记对象已经和当前的会话建立了关联,由于应用对对象做了保存的操作, Session 还要在 insertions 中登记应用的这个插入行为(行为包括:对象引用.对象 id . Session .持久化处理类).  (3) s.evic

JMeter学习笔记--JMeter执行顺序规则

JMeter执行顺序规则: 配置元件 前置处理器 定时器 采样器 后置处理器(除非服务器响应为空) 断言 监听器 只有当作用域内存在采样器时,定时器.断言.前置/后置处理器才会被执行,逻辑控制器和采样器按照在测试树种出现的顺序执行.其他测试元件会依据自身的作用域来执行,另外与测试元件所属的类型有关(归属于同一类型的测试元件,会按照他们在测试树中出现的顺序来执行. 测试计划实例如下: Controller post-Processor1 Sampler1 Sampler2 Timer1 Asser

shell学习四十八天----进程建立

进程 前言:进程指的是执行中程序的一个实例.新进程由fork()与execve()等系统调用所起始,然后执行,知道他们下达exit()系统调用为止. linux系统都支持多进程.虽然计算机看起来像是一次做了很多事,但除非是他拥有多个CPU,否则一次做了好多事只是个错觉.事实上,每个进程仅容许在一个极短的期间执行,我们称为时间片段,之后进程会先暂时搁置,让其他等待中进程执行.时间片段极短,通常只有几微妙,所以人们很少感觉到进程将控制权交回内核,再交给另一个进程的这种文本切换.进程本身不会管理文本切

鸟书shell学习(三)shell脚本程序设计要点总结

一 注意事项 1.脚本的开头一行为 : #!/bin/bash 声明这个脚本需要的shell的名称 2.程序中出了"#!"之外的其他以#开头的都是注释内容 3.为了方面的使用系统命令,可以在程序的开始部分设置主要环境变量例如PATH 4.脚本程序的运行方法: sh example.sh 或者类似 bash example.sh chmod +x example.sh ; ./example.sh    ################################ 这两种执行方式都是

shell学习三十天----break,continue,shift,getopts

break和continue 这两个命令分别用来退出循环,或跳到循环体的其他地方. 使用while与break,等待用户登录 bash代码: printf "Enter username: " read user while true do if who | grep "$user" >/dev/null then break; fi sleep 30 done 等待特定用户,每30秒确认一次 true命令什么事也不必做,只是成功的退出.这用于编写无限循环,即

shell学习三十五天----波浪号展开与通配符

波浪号展开与通配符 shell中两种与文件名相关的展开.第一种是波浪号展开,第二种是通配符展开式. 波浪号展开 如果命令行字符串的第一个字符为波浪号(~),或者变量指定(例如PATH或CDPATH变量)的值里任何未被引号括起来的冒号之后的第一个字符为波浪号(~)时,shell变回执行波浪号展开. 波浪号展开的目的,将用户根目录的符号型表示方式,改为实际的目录路径.可以采用直接或间接的方式指定执行此程序的用户,如未明白指定,则为当前的用户: 命令:vi ~/.profile       与vi $

shell学习三十一天----函数问题

函数 案例一: #!/bin/bash hello () { echo "hahahah" } hello 执行函数,结果为:hahaha 案例二: #!/bin/bash funWithReturn() { echo "the function is to get the sum of two number" read -p "input first number" num1 read -p "input second number&