首页 > 代码库 > 《学习bash》笔记--基础shell编程

《学习bash》笔记--基础shell编程

1.shell脚本和函数

脚本是包含shell命令的文件,它是一个shell程序,有三种方式运行它们:
一是键入source scriptname,使得脚本的命令被读取并运行,就好像键入它们一样。
二是运行bash scriptname,打开一个子shell来读取并执行脚本文件中命令。该脚本文件可以无"执行权限"。
三是使用./scriptname,打开一个子shell读取并执行脚本文件中的命令,该脚本需要“执行权限”。

1.1.函数

函数是一种脚本内脚本,你使用它通过名字来定义某些shell代码,并将其保存在shell内存中以便以后进行调用和运行。
要定义一个函数可以使用下述两种格式:
function funcname
{
shell commands
}
或者
funcname()
{
shell commands
}
两者没有功能上的区别。可以使用命令unset -f funcname删除一个函数定义。

键入declare -f命令找到登录会话中定义的函数,该命令打印函数名和函数定义,如果只需要打印函数定义,可以使用
declare -F命令。

在shell中键入一个命令时,各种资源的优先级次序如下:
1.别名
2.关键字,例如 fucntion
3.函数
4.shell的内置命令,如cd和type
5.脚本的可执行程序。shell按在PATH环境变量中列出的目录中队其进行搜索。
可以使用内置的command、builtin和enable百变优先级次序,会在以后进行介绍。

可以使用type命令查看命令的特定细节:
如果使用type  command,按照上面的优先级次序查找命令的信息,找到其中一个后就结束。
如果使用type -all command,按照上面的优先级次序查找所命令的信息,显示所有资源的信息。
如果使用type -type command,输出单个单词描述符:关键字,函数,内置命令或者文件。


2.shell变量

2.1.位置参数

可以使用varname=value的语句定义变量值,例如:

hatter=mad
 echo "$hatter"
mad

最重要的特定内置变量称为位置参数,当脚本被调用时,它们保存脚本的命令参数。位置参数名为1,2,3等,其值由$1,$2,$3表示。

还有一个位置参数0,其值为脚本名。

还有两个特殊变量包含了所有的位置参数(出了位置参数0):*和@。它们的差别不明显但很重要,并且只有在双引号内才体现出来。

“$*”是包含所有参数位置的单一字符串,由环境变量IFS(内部域分隔符,internal field seperator)中第一个字符分隔。IFS默认为

空格、TAB和NEWLINE。另一方面,“$@”等价于“$1”"$2"...."$N",这里N为位置参数数目,等价于N个单独的由空格分隔的双引号

字符串。如果没有位置参数,“$@”为空。

最后“$#”是参数的数量。


使用如下脚本params进行测试:

echo "$@"
echo "$*"
echo "$0 $1 $2 $3"
echo "$#"

执行的结果是:

./params 1 2 3
1 2 3
1 2 3
./params 1 2 3
3


2.2.函数内位置参数

shell函数使用位置参数和特殊变量,如*和#,其方式与shell脚本一样,典型情况下,在一个shell脚本里都要定义几个shell函数,

因此每个函数都要处理自己的参数,这表明每个函数都要分别跟踪位置参数,每个函数都有这些变量的副本,我们称这些变量

对函数是局部的。

然而,在函数内定义的其他变量则不是局部的,其取值爱真个shell脚本中均为已知。

使用如下脚本进行测试:

func_a()
{
echo "$0,$1,$2"
var1="var1 in func_a"
echo "var1:$var1"
}

var1="var1 out func_a"
echo "var1:$var1"
func_a arg1 arg2
echo "var1:$var1"
echo "$0,$1,$2"

执行脚本:

$./var.sh 1 2

var1:var1 out func_a
./var.sh,arg1,arg2
var1:var1 in func_a
var1:var1 in func_a
./var.sh,1,2

在函数外定义的var1被函数内的赋值给覆盖了。


2.3.函数内局部变量

函数定义中的local定义使所涉及的变量均为函数的局部变量。将上面的脚本修改如下:

func_a()
{
echo "$0,$1,$2"
local var1="var1 in func_a"
echo "var1:$var1"
}

var1="var1 out func_a"
echo "var1:$var1"
func_a arg1 arg2
echo "var1:$var1"
echo "$0,$1,$2"

执行结果:

$./var.sh 1 2

var1:var1 out func_a
./var.sh,arg1,arg2
var1:var1 in func_a
var1:var1 out func_a
./var.sh,1,2

此时在函数内的var1变成了局部变量。


2.4.对$@和$*进行引用

为什么“$*”用IFS的第一个字符而不是用空格分隔呢,这时为了输出的灵活性。下面是一个简单的例子,打印逗号分隔的位置参数

列表,脚本为:

IFS=,
echo "$*"

运行结果为:

$./a.sh 1 2 3
1,2,3

为什么“$@”用作N个独立的双引号引用字符串呢?目的是允许你再次分别使用它们。


2.5.变量语法详解

取一个变量值的$varname的语法实际上是常用语法${varname}的简化形式。

为什么要有两种语法呢,一个原因是,如果代码中引用了多余9个位置参数,则必须使用${10}而不是$10.

使用如下脚本进行测试:

echo "$0 $1 $2 $3 $4 $5 $6 $7 $8 $9 $10 $20"

运行结果:

$./params 1 2 3 4 5 6 7 8 9 0

./params 1 2 3 4 5 6 7 8 9 10

$10被解释为$1后面跟一个字符0,所以$10为10.

还要考虑下面情况,如果要在用户ID后面放一个下划线。

echo $UID_

shell会试图使用UID_作为变量名。必须使用${UID}_


3.字符串操作

替换操作符:

${varname:-word}

如果varname存在并且非null,返回其值,否则返回word。

${varname:=word}

如果varname存在并且非null,返回其值,否则将其设置为word,然后返回其值。位置参数和特殊餐宿不能这样设置。

${varname:=?message}

如果varname存在并且非null,返回其值,否则打印varname:message,并推出当前命令或脚本。省略message则产生默认

信息:parameter null or not set。

${varname:+word}

如果varname存在并且非null,返回word,否则返回null。

${varname:offset}

${varname:offset:length}

执行字符串扩展。返回$varname从offset开始,长度为length的子字符串。如果长度省略,字符串从offset开始,一直到$varname

结束。如果varname为@,length则为从参数offset开始的位置参数的数目。


模式匹配操作符:(模式可能包含任意字符的字符串,用于字符设置和范围的*,?和[])

${variable#pattern}

如果模式匹配变量取值的开头,删除最短的匹配部分,返回剩余部分。

${variable##pattern}

如果模式匹配变量取值的开头,删除最长的匹配部分,返回剩余部分。

${variable%pattern}

如果模式匹配变量取值的结尾,删除最短的匹配部分,返回剩余部分。

${variable%%pattern}

如果模式匹配变量取值的结尾,删除最长的匹配部分,返回剩余部分。

${variable/pattern/string}

${variable//pattern/string}

将variable中匹配模式的最长部分替换成string。第一种格式中,只有第一个匹配部分被替换,第二种格式中,所有匹配部分均被

替换。如果模式以#开头,必须匹配variable的开头。如果以%开头,则必须匹配variable的结尾。如果string为null,匹配部分被删除。

如果variable为@或*,操作为依次应用于每个位置参数并且扩展为结果列表。


长度操作符:
${#varname}返回变量字符串的长度。


4.命令替换

命令替换允许你使用命令的标准输出,就好像它是一个变量值一样。命令替换的语法是:
$(command)

运行圆括号内的命令,该命令写到标准输出的内容返回作为表达式值。