shell执行命令的步骤顺序如上图,看起来有些复杂。

当命令行被处理时,每一个步骤都是在Shell的内存里发生的;Shell不会真的把每个步骤的发生显示给你看。

所以,你可以假想这事我们偷窥Shell内存里的情况,从而知道每个阶段的命令行是如何被转换的。

shell执行命令的原理

  • Shell 从标准输入或脚本中读取的每一行称为管道(pipeline);它包含了一个或多个命令(command),这些命令被一个或多个管道字符(|)隔开。
  • 对于每一个读取的管道,Shell都会将命令分割,为管道设置 I/O,并且对每一个命令依次执行下面的操作:
  • 将命令分割成令牌(token),令牌之间以元字符分隔。
  • Shell的元字符集合是固定不变的,包括空格、Tab键、换行字符、分号(;)、小括号、输入重定向符(<)、输出重定向符(>)、管道符(|)和 &符号,令牌可以是单词(word)、关键字,也可以是I/O重定向器和分号。
  • 检查第一个token是不是一个不带(引号或反斜杠)开放的关键字,if、while、for或其他控制结构中的开始符号
  • 如果是Shell就认为此命令是复合命令,并为该复合命令进行内部设置,读取下一条命令,再次启动进程。
  • 如果此令牌不是复合命令的开始符号,如该令牌是then、else、do、fi、done等符号,这说明该令牌不应该处在命令行的首位,因此,Shell提示语法错误信息。
  • 按别名检查每个命令的第一个关键字是不是一个别名,如果是,则用其别名定义替换,然后回退到第一步

执行花括号 { } 扩展

执行波浪号 ~ 扩展

执行变量扩展

执行命令替换

执行算术表达式计算

把生成的新命令按IFS分割成token

执行路径扩展

按优先级查找命令,先从内置,再从path

设定好重定向等,执行命令

花括号扩展

简单点说就是给一组字符串加上相同的前缀和后缀,生成一组新的字符串。前缀和后缀都可以为空

sh-4.2# echo a{b,c}d
sh-4.2# echo a{b,c}

可以使用一个范围,只支持数字和字母

sh-4.2# echo a{..}
a1 a2 a3 a4 a5 a6 a7 a8 a9 a10
sh-4.2# echo a{b..l}
ab ac ad ae af ag ah ai aj ak al

花括号还可以嵌套,逐层有序的进行处理

sh-4.2# echo a{{,},{b,c}}
a1 a2 ab ac

这个过程可以理解为先生成a{1,2,b,c},然后生成 a1 a2 ab ac

要注意的地方

1. 花括号里只有字面量。不支持变量:

sh-4.2# a=
sh-4.2# echo a{$a..}
a{..}

并没有像之前一样生成1,2,3,4..10。对花括号扩展来说,生成的结果是{$a..10},那为什么我们看到的结果是{1..10}呢?这就是shell的命令处理顺序有关系了,我们在看shell的命令处理顺序,花括号扩展是在第4布,到了第6步,才会执行变量扩展,这时候相当于命令

echo {$a..10}

1

10

先执行 echo{$a..10},在执行变量扩展 $a被替换成1,因此输出 {1..10}

花括号里至少要有一个逗号,至少要有两项,两项之间不能有空格,逗号前后不能有空格,否则不进行花括号扩展

波浪号扩展

波浪号扩展就是对 ~ 进行处理。

一般情况下,我们认为 ~ 代表了当前的主目录。但是并非这么简单。进行波浪号替换的条件是很苛刻的

首先,进行波浪号扩展的前提是波浪号必须位于一个 token 的开头,简单的说 ~ 前面应该是空格

然后,shell会分析波浪号之后,第一个 / 或 : 之前的未被引号括起来的字符串(如果没有 / 那就取波浪号之后的所有字符)这个字符叫做”波浪号前缀(tilde-prefix)”(注意,所谓波浪号前缀其实是出现在波浪号后面的),波浪号前缀的取值和对应的处理方式是:

如果波浪号前缀是个有效用户名,则波浪号和波浪号前缀一起替换成用户的主目录

如果波浪号前缀为空,则尝试把波浪号替换成HOME,如果HOME没有被设置,则将波浪号替换成当前用户主目录

如果波浪号前缀是 + 则 ~+ 被替换成当前工作目录(pwd)

如果波浪前缀是 - 则 ~- 被替换成上一个工作目录 (OLDPWD)

如果波浪号前缀是个数字 n 则把 ~n 替换成目录堆栈 (用 dirs 命令可以查看目录堆栈)的第n个元素(这个没有什么用)

sh-4.2# echo ~
/root

波浪号被替换成当前用户主目录

sh-4.2# echo /~
/~

波浪号不在token的开头,不进行扩展

sh-4.2# echo /~
/~
sh-4.2# echo ~root
/root
sh-4.2# echo ~root/
/root/
sh-4.2# echo ~+
/root

波浪号被替换成当前工作目录

sh-4.2# echo ~-
~-
sh-4.2# cd /
sh-4.2# echo ~-
/root

变量扩展

这个大家最熟悉了,$真是个好东西,变量扩展,命令替换,算术扩展都离不了它(当然还能买东西)。一般情况下我们习惯使用$var,其实正规的格式是${var}。前一种形式更简便,后一种更强大,很多时候必须用后一种形式才行。

先说间接引用,这东西很像C语言里的指针

sh-4.2# a=b

sh-4.2# b=1

sh-4.2# echo ${!a}

其他的变量操作,列表看到的更清晰

${var:-word}   如果变量var已被设置且非空,则代入它的值,否则带入word

${var:+word}   如果var已被设置且值非空,带入word,否则什么都不带入(带入空)

${var:?word}   如果var已被设置且值非空,就带入它的值,否则打印word并退出shell。省略word会输出:parameter null or not set

注意:上面word可以是一个变量,使用$word的形式引用其值

${var:offset} 获取var中offset开始的字串

${var:offset:length} 获取var中offset开始长为length的字串。

注意:上面的offset和length可以使变量,使用offset,offset,length引用其值

${#var} 替换为变量中字符个数,如果var是* ,@或数组,长度则是位置参量的个数。

${var%pattern} 把字符串尾部与模式进行最小匹配,并删除匹配到的部分。

${var%%pattern} 把字符串尾部与模式进行最大匹配,并删除匹配到的部分。

${var#pattern} 把字符串头部与模式进行最小匹配,并删除匹配到的部分。

${var##pattern} 把字符串头部与模式进行最大匹配,并删除匹配到的部分

${var/pattern/string} 使用string替换pattern的最大匹配部分。

如果pattern以/开头则进行全部替换,否则只替换第一个匹配的位置。如果pattern以#开始,则起始部分必须匹配,如果以%开始则结尾部分必须匹配

注意:

上面的pattern可以使变量,使用$pattern引用其值。

如果var是 、@ 或数组,且以下标为 * 或 @ 的形式出现,则对其中每一个元素都进行匹配操作。

命令替换

用命令的输出来替换命令本身。有两种形式$(cmd)和`cmd`,推荐前一种形式,后一种形式是old-style了。

算术扩展

用算术表达式的值替换算术表达式本身。格式$((expr))。expr是个表达式,如4+3。理解起来比较简单。不过关于expression,bash有自己特定的支持,某些运算它是做不了的。

sh-4.2# echo $((+))

参考文档:https://blog.csdn.net/winsolstice/article/details/74515885

Shell命令的执行顺序的更多相关文章

  1. linux shell的配置文件执行顺序

    shell配置文件的作用:初始化环境变量.设置命令提示符.指定系统命令路径等 shell配置文件分类: (1)系统级别配置文件: /etc下,比如/etc/profile./etc/bashrc (2 ...

  2. SHELL脚本--多命令逻辑执行顺序

    bash&shell系列文章:http://www.cnblogs.com/f-ck-need-u/p/7048359.html Linux中可以使用分号“;”.双and号“&& ...

  3. shell 脚本控制命令的执行顺序

    &&,||,(),{},& 五个符号的运用shell脚本执行命令的时候,有时候会依赖于前一个命令是否执行成功.而&&和||就是用来判断前一个命令执行效果的. 也 ...

  4. &&、()、||决定linux命令的执行顺序

    在执行某个命令时,有时需要依赖前面的命令是否执行成功.假如你想通过ssh命令复制很多数据文件到另外的机器后,删除所有源有文件,所以在删除源有文件之前首先要确定复制是不是执行成功.只要执行复制成功才可以 ...

  5. Linux学习之路:命令别名、历史记录和命令查找执行顺序

    一.命令别名 alias rm='rm –i':删除命令时会随时出现提示;alias vi=vim alias 不加参数,显示系统内所以命令别名 unalias 取消别名 二.历史命令 history ...

  6. Shell命令的执行优先级

    Shell内置命令.外部命令.别名.函数.保留关键字的优先级 在Shell中,有5种可调用的东西:别名(alias).函数(function).shell保留关键字.shell内置命令.外部命令. 如 ...

  7. python 对shell 命令的 执行 逻辑 在一台机器上执行另一台机器的命令; 跨节点 执行命令

    import os l = ['ssh a;scp /data/visitlog/*11* root@d:/data/mapReduceVisitorLog/'] # b c for i in l: ...

  8. Samba远程Shell命令注入执行漏洞

    CVE:CVE-2007-2447 原理: Samba中负责在SAM数据库更新用户口令的代码未经过滤便将用户输入传输给了/bin/sh.如果在调用smb.conf中定义的外部脚本时,通过对/bin/s ...

  9. shell命令行执行python(解析json)

    每个脚本都有自己的擅长. 有次实现一个work,使用了shell,php,python看着文件种类多,不方便交接,看着也比较麻烦. 减少文件种类数,也是很有必要的. 遇到的场景:shell程序需要从j ...

随机推荐

  1. M - 约会安排 HDU - 4553 线段树 (最长连续段)

    中文题面 思路:维和两个区间  一个是女神区间 一个是基友区间  如果是基友要预约时间 直接在基友区间查询可满足的起点 (这里先判tree[1].m >=length也就是有没有这样的区间满足时 ...

  2. Android Studio导入jar包

    使用开源框架是,可以直接复制源代码到自己的项目(本人在Android Studio中操作报R程序包不存在),也可以使用jar包,下面记录一下今天使用SmartImageView.jar的过程,不记录S ...

  3. thymeleaf中的判断总结

    判断String字符串,添加引号 th:class="${flag=='forum.html'}?'active'" 判断boolean类型,注意不能当成字符串处理,不能添加引号 ...

  4. 微信小程序原生开发简介

    简介: 总结: 1. 逻辑层使用js引擎,视图层使用webview渲染 2. 微信小程序已经支持了绝大部分的 ES6 API 3. 可以自动补全css的兼容语法 文档:https://develope ...

  5. 【HDU 6171】Admiral(搜索+剪枝)

    多校10 1001 HDU 6171 Admiral 题意 目标状态是第i行有i+1个i数字(i=0-5)共6行.给你初始状态,数字0可以交换上一行最近的两个和下一行最近的两个.求20步以内到目标状态 ...

  6. HNOI2019总结

    HNOI2019总结 Day 1 开场看三道题,T1是个计算几何,T2是个操作树加\(border\),T3题意有点复杂.想T1想了半个多小时,发现那个钝角不是很会处理,但是40分暴力应该还是可以写, ...

  7. nfs的配置文件/etc/exports

    /etc/exports  文件格式 <输出目录> [客户端1 选项(访问权限,用户映射,其他)] [客户端2 选项(访问权限,用户映射,其他)] a. 输出目录:输出目录是指NFS系统中 ...

  8. 【BZOJ4911】[SDOI2017]切树游戏(动态dp,FWT)

    [BZOJ4911][SDOI2017]切树游戏(动态dp,FWT) 题面 BZOJ 洛谷 LOJ 题解 首先考虑如何暴力\(dp\),设\(f[i][S]\)表示当前以\(i\)节点为根节点,联通子 ...

  9. sublime text3 replace和反向引用

    实用小技巧,主要用于替换爬虫请求头,节省时间. chrome原信息显示: UserID: sds UserPass: sdsd codeKey: 350753 code: 277 B1: 提 subl ...

  10. ArcGIS for qml - 地址地标转换为经纬度(地理编码)

    实现输入地址地标转换为其经纬度 本文链接:地理编码 作者: 狐狸家的鱼 Github: 八至 一.地理编码 1.地理编码含义 地址编码(或地理编码)是使用地址中包含的信息来插入地图上的相应位置的过程. ...