Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1714735
  • 博文数量: 186
  • 博客积分: 3044
  • 博客等级: 中校
  • 技术积分: 2493
  • 用 户 组: 普通用户
  • 注册时间: 2006-03-25 15:04
文章分类

全部博文(186)

文章存档

2024年(2)

2022年(4)

2021年(3)

2020年(1)

2019年(5)

2018年(13)

2017年(6)

2016年(10)

2015年(11)

2014年(11)

2013年(13)

2012年(23)

2011年(25)

2010年(2)

2008年(1)

2007年(5)

2006年(51)

分类: LINUX

2012-04-13 11:50:33

shell参数展开

引子

在写Shell脚本的时候,不可避免地要用到变量,也常常要对变量进行一些基本的处理。比如说前两天在给人演示*nix系统的方便之处时,我举了个文件名批量修改的例子(好吧。。这是个不怎么恰当的例子。。),比如说我们想把.h结尾的文件改成.hpp,那么可以这么做

find . -regex ".*\.h$" -exec rename 's/.h$/.hpp' {} \;

原理很简单:先找到以”.h”结尾的文件,然后调用rename将结尾的”.h”改成”.hpp”。但是在演示的时候杯具出现了: Snow Leopard默认是没有rename这个程序的。-.-||| 没办法,只好想其他办法:

 

find . -regex ".*\.h$" | while read i; do mv $i ${i%.h}.hpp; done;

这个虽然也可以完成任务,但是看上去就比上一条命令复杂多了,尤其是”${i%.h}.hpp”这种用法,显然不如’s/\.h$/\.hpp’看上去直观。虽然之后又想到了其他的替代方案,比如:

find . -regex ".*\.h$" | while read i; do mv $i $(basename $i h)hpp; done;

但像${i%.h}这种用法确实也会经常碰到,于是我就发扬了下打破沙锅问到底的优秀品质,想搞清楚这种类似的用法,到底还有哪些呢?

Shell Parameter Expansion

其实这种用法是有专有名词的,就叫做Shell参数展开(Shell Parameter Expansion)。感兴趣的同学可以直接看GNU Bash Manual中的。

其实最基本的参数展开像这样:

${parameter}

shell会将上述表达式转换为parameter的值,即我们通常用的$parameter. 其中parameter是参数名,之所以用”{}”括起来是为了确保shell不会将紧跟其后的字符也作为变量名的一部分,从而尽可能地避免杯具的发生。比如说:

$ var="Hello, Bread"
$ echo ${var}
Hello, Bread

细心的同学可能会问其实这里说的参数(parameter)不就是我们通常说的变量 (variable)么? 嗯。。其实大部分时候这俩名词的意思基本等同。只不过在Shell中parameter(参数)是variable(变量)的超集: 变量名不能以数字开头,而参数名可以。比如说$1就表示命令行传入的第一个参数。

除去最基本的参数展开之外,还有很多种比较灵活的用法如下:

1. 间接展开
${!parameter}

相当于${var},而var=${parameter}。比如说:

$ param="var"
$ var="Hello, Bread"
$ echo ${!param}
Hello, Bread
2. 参数长度
${#parameter}

作用: Shell会将其替换为$parameter的长度。
例子:

$ var="Hello"
$ echo "len($var) is "${#var}
len(hello) is 5
3. “掐头去尾”

截取又分两种操作,”掐头”和”去尾”。与正则表达式中不同,头和尾在这里对应的符号分别是”#”和”%”。

掐头:

${parameter#word}${parameter##word}

作用: 从parameter头部开始匹配word,并删除成功匹配的部分。在构造word时可以使用”*”表示任意长度字符,”?”表示单位长度字符,并可用形如”[a-c]“的方式来指定匹配”abc”中的任意字符。

另外,”#”和”##”的区别在于前者是最短匹配,而后者是最长匹配;实际上就是正则表达式中的”懒惰”和”贪婪”的概念。下面的例子能够很清楚地看出这两者的区别。比如说:

$ var=brbread
$ echo ${var##*br}
ead
$ echo ${var#*br}
bread

去尾:

${parameter%word}${parameter%%word}

作用: 与掐头相同,唯一不同的是从$parameter的尾部开始匹配。
例子:

$ var="La.Maison.en.Petits.Cubes.avi"
$ echo ${var%.*}
La.Maison.en.Petits.Cubes
$ echo ${var%%.*} La

匹配示例中的”.*”时, shell会从$var的尾部开始查找”.”,如果是最短匹配(第一个示例),则找到第一个”.”就停止,否则(第二个示例)会一直找到最后一个”.”才 停止。可以看到,这种用法可以很方便地去掉文件后缀,从而得到文件名,正是本文开头所用到的方法。

4. 字符串替换

格式:

${parameter/pattern/string}

作用: 将$parameter中出现的第一个pattern替换为string。值得注意的是,除了”*”, “?” 和”[]“以外,pattern的头部还可以使用下面几个字符:

“/”: 如果pattern以”/”起始,则所有的匹配项都要替换。而默认的行为只是替换最左侧的一个。
“#”: 如果pattern以”#”起始,则与正则表达式匹配规则相同,只有在$parameter的头部找到匹配项才会进行替换。
“%”: 与”#”类似,只是这次变成了尾部匹配。

例子:

$ var="see"
$ echo ${var/e/a} #只有第一个e会被替换
sae
$ echo ${var/ee/it}
sit
$ echo ${var//e/a} #所有的e都会被替换
saa
$ echo ${var/#e/a} #头部的e才会被替换
see
$ echo ${var/#s/b}
bee
$ echo ${var/%e/a}
sea
5. 截取子串

格式:

${parameter:offset:length}

作用: 从offset处开始,截取$parameter中长度为length的子串。其中offset如果为负数的话,表示从尾部开始数。并且要注意这时 候”:”和offset之间至少要有一个空格,不然shell会当成”:-”处理,这个是后面要介绍到的空参数处理操作符。比如:

$ var="hello"
$ echo ${var:2:2}
ll
$ echo ${var: -3:2}
ll
$ echo ${var:-3:2}
hello
6. 查找参数名
${!prefix*}

Shell会自动将其展开为所有以prefix开头的参数名。如:

$ var1=
$ var2=
$ echo ${!var*}
var1 var2
7. 空参数处理

在这个部分介绍的操作符都是与空参数相关的,而空参数就是值为空,或unset掉的参数。比如说:

$ var=

$ unset var

这样操作后的参数var就是一个空参数。

${parameter:-word}

parameter不为空则用$parameter,不然就用word。例子:

$ unset bread
$ echo ${bread:-hello}
hello

这样实际上是为parameter提供了一个默认值。类似swith..case中的default语句。

${parameter:+word}

如果parameter不为空,就返回word。
例子:

$ var="hello"
$ echo ${var:+"var is not null"}

除了上面介绍到的之外,shell参数展开还有很多其他的用法,比如说转换大小写,返回有效的数组下标等等。具体的请参见GNU bash manual.

 

另外再介绍篇文章:

Linux tip: Bash parameters and parameter expansions 
出处:http://www.ibm.com/developerworks/library/l-bash-parameters.html?ca=dr
阅读(4472) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~