Tips for shell newbie (1)

Tip 1. 注意shell执行的具体类型

Shell脚本的第一行(Shebang line, e.g. #!/bin/bash)指定了其执行的具体类型,dash/bash/ksh/…等等,之间会有些细微的区别。如果你的脚本没有Shebang line,那么由系统变量$SHELL来决定其执行类型。

PS1:/etc/shells中记录着系统可用的shell类型。所以当你发现同样的脚本在不同系统表现不同的时候,有可能是dash和bash(或是其他shell)之间的区别。

PS2:sh是一种POSIX规范,而众多的shell类型是它的实现。linux系统中的sh一般是个symlink,例如ubnutu中默认的sh就会指向/bin/dash。

Tip 2. 赋值语句两边的空格

a = 'test',这条语句是错误的。因为在shell里赋值语句=的两边是不能有空格的,a=test才是正确写法。这个错误几乎是初学者必犯的错误了。。。

但是在判断语句中[]两侧则必须留空格:if [a == 'test']是错误的;if [ a == 'test' ]是正确的。

Tip 3. 各种预留的$变量$*表示参数列表$0, $1, $2…$#表示参数列表个数$?表示上一条命令的执行结果$!表示上一条命令的PID号

PS1:对于整个shell来说,参数列表是运行时接的各个参数;对于shell中的函数来说,参数列表是调用时传入的各个参数。

PS2:对于test.sh '1 2' 3 4来说,$*$@是等价的(均包含1 2 3 4四个参数)。"$*""$@"则不同,"$*"是一个参数1 2 3 4,而"$@"为三个参数1 2 3 4)在。Google的shell code规范推荐使用$@

Tip 4. 检查文件/文件夹是否存在等等if [ –d ${dir} ]判断文件夹是否存在if [ –f ${file} ]判断文件是否存在……

这里想说的是如何查询诸如此类判断条件的说明:man [man test即可。

Tip 5. [ … ][[ … ]]

[ … ]指的是标准POSIX规范中的条件判断,而[[ … ]]是对POSIX规范的扩展。

这些区别有:(注意:对POSIX Shell适用的对Extended POSIX Shell均适用,反之则不然)

POSIX Shell Extended POSIX Shell 判断字符串相等 = == 多条件判断 -o || -a && 正则匹配 N/A =~

Tip 6. 替换if判断语句

if [ ! false command ]; then   dosomthing

可以用false command || dosomething替代。同理:

if [ true command ]; then  dosomthing

可以用true command && dosomething替代。

Tip 7. 将执行结果赋予变量

有两种方式:var1=`command` 或者 var1=$(command)。Google的shell code规范推荐后者。

Tip 8. 变量替换${var1-str}判断当var1为空时,返回str(不改变var1的值)${var1+str}判断当var1不为空时,返回str(不改变var1的值)${var1=str}判断当var1为空时,返回str,且将str赋给var1${var1?str}判断当var1为空时,在错误输出中输出str

PS:也有在var1后加上:的写法,二者大部分情况下是等价的。除了一个变量被定义但没赋值的情况(var1=),这种情况下加:会报错。

${var1:pos}返回第pos个字符后的值${var1:pos:len}返回第pos字符后,长度为len的值${var1/pattern/replacement}将var1匹配到的第一个pattern替换为replacement并返回${var1//pattern/replacement}将var1中的所有pattern替换为replacement并返回${var1/#pattern/replacement}相当于标准正则中的^pattern,即匹配的pattern必须在var1首部

${var1/%pattern/replacement}相当于标准正则中的pattern$,即匹配的pattern必须在var1尾部

${var1#pattern}将var1中从前往后遇到的第一个尽可能短的pattern删除并返回

${var1##pattern}将var1中从前往后遇到的第一个尽可能长的pattern删除并返回${var1%pattern}将var1中从后往前的第一个尽可能短的pattern删除并返回${var1%%pattern}将var1中从后往前的第一个尽可能长的pattern删除并返回

以上操作均不改变var1的原始值(即不带-i的sed)。实际上以上的所有操作都能通过sed完成,但我的习惯是能用原生的shell替换,就不再调一个sed。例如你要把某文件的后缀名删掉,使用${file%.*}而不是echo "$file" | sed 's/\.[^.]*$//'

Tip 9. 数值计算与判断

因为shell中没有原生的浮点数,以下所说的数值均是指整数。总体来说,赋值/计算的方法有三种:

letexpr(())(推荐)

#!/bin/bashfoo=1let bar1=foo+1let bar2=$foo+2bar3=$(expr $foo + 3) #不能用foobar4=$((foo+4)) #不能用$fooecho $bar1$bar2$bar3$bar4 # 输出2345

关于数值的比较,初学者很容易犯错,想当然的使用==, <=等等。实际上bash必须使有特定的数值比较符:-eq, -ne, -lt, -gt, etc。

Tip 10. 比较字符串/数值的大小

先来看一段代码:

foo="9.0"bar="10.0"if [ $foo > $bar ]; then  echo "Unexpected"else  echo "Expected"fi

你觉得它会输出什么?Expected还是什么?这里其实有两个坑:

    这里的>不是你所想当然的比较符,它其实是重定向(更确切的说是清空它所接的参数文件的内容,如果没有则创建一个空文件)。所以这个if执行的效果就是在当前目录生成一个叫做10.0的空文件,返回0作为这个if的判断结果,然后输出Unexpected。如果你认为按照Tip 5所说,将[ $foo > $bar ]改为[[ $foo > $bar ]]就万事大吉,那就掉进了另一个坑。在Tip 9中说到:shell中是没有原生的浮点数的,这里的foobar均是按照字符串来对待的。也就是说这个if的比较方式是从高到低,依次比较每个字符:那么很显然9>1,所以仍然会输出Unexpected

Go to Tip 11 – Tip 20

Tips for shell newbie (1)

相关文章:

你感兴趣的文章:

标签云: