shell脚本中定义变量的方式很自由(弱类型),直接使用:var_name=var_value 就行了。获取其值也简单,只需要在值前面添加$符号:echo "${var_name}"。shell运行时,有些变量根据当前运行环境已经内置好了,十分方便我们使用。

位置参数:当脚本被调用时,他们保存脚本的命令行参数。位置参数名字为0、1、2、3.....其值由$0、$1、$2...表示。其中0代表当前运行的脚本名字,1、2、3...分别代表对应的命令行参数。

vim test.sh

#!/bin/bash
echo "shell name:$0"
echo "param 1:$1"
echo "param 2:$2"

chmod +x test.sh
./test.sh a b

shell_name:./test.sh
param 1:a
param 2:b
vim test.sh

#!/bin/bash
echo "shell name:$0"
echo "param 1:$1"
echo "param 2:$2"

chmod +x test.sh
./test.sh a b

shell_name:./test.sh
param 1:a
param 2:b

还有两个家伙包含了所有的位置参数(除了0以外),那就是*跟@了。他们两的区别不是很明显:


$* 包含了所有的位置参数,他们之间通过环境变量IFS(内部域分割符,internal field seperator)中的第一个字符进行分隔开。IFS默认为空格。这里为什么要使用IFS而不直接使用空格符,这是为了输出的灵活性:

test.sh

#!/bin/bash
echo "\$*:$*"
IFS=-
echo "new \$*:$*"

chmod +x test.sh
./test.sh a b c
$*:a b c
new $*:a-b-c
test.sh

#!/bin/bash
echo "\$*:$*"
IFS=-
echo "new \$*:$*"

chmod +x test.sh
./test.sh a b c
$*:a b c
new $*:a-b-c

$@里面也包含了全部的位置参数了,其组织形式类似于:$@="$1" "$2" "$3"....为什么要这样,目地就是为了再次使用他们。

test.sh

#!/bin/bash
function count_params
{
    echo "$#"
}
count_params "$*"
count_params "$@"

chmod +x test.sh
./test.sh a b c

1
3
test.sh

#!/bin/bash
function count_params
{
    echo "$#"
}
count_params "$*"
count_params "$@"

chmod +x test.sh
./test.sh a b c

1
3

还有就是$#代表当前的参数个数,$?代表上一次操作的返回结果。

关于函数内的位置参数:

shell函数里面也使用位置参数跟特殊变量(# @ *等),使用方式跟shell脚本的一样,而且函数内这些变量属于函数局部的($0除外),由函数自己管理,与其他函数或者主shell的同种变量无关,类似于其他高级语言调用函数时的局部变量压栈,这个解决了传参的问题。但是,特殊的是,除了这些预定义的变量外,其他用户自定义变量都是全局属性的。多说无谓,上例子:

test.sh

function afunc
{
    echo in function:$0 $1 $2
    var1="in function"
    echo var1:${var1}
}
var1="outside function"
echo var1:${var1}
echo $0:$1 $2
afunc funarg1 funarg2
echo var1:${var1}
echo $0:$1 $2

chmod +x test.sh
./test.sh arg1 arg2

var1:outside function
./test.sh:arg1 args2
in function:./test.sh funarg1 funarg2
var1:in function
var1:in function
./test.sh:arg1 args2
test.sh

function afunc
{
    echo in function:$0 $1 $2
    var1="in function"
    echo var1:${var1}
}
var1="outside function"
echo var1:${var1}
echo $0:$1 $2
afunc funarg1 funarg2
echo var1:${var1}
echo $0:$1 $2

chmod +x test.sh
./test.sh arg1 arg2

var1:outside function
./test.sh:arg1 args2
in function:./test.sh funarg1 funarg2
var1:in function
var1:in function
./test.sh:arg1 args2

很明显函数里面的var1的改变直接影响到shell里面的var1的值了。要使函数里面的var1变成局部,只需要在其前面加上local关键字就行了。 shell中获取变量的值其实应该使用大括号操作,以避免一些不必要的错误。比如,shell要接受10个参数,那第十个参数就要用${10}来表示而不是$10(等于${1}0)了。


大括号操作可不仅仅有这个功能,它还提供了强大的字符串操作,用来对变量的值进行各种处理以获得想要的值。

有几种对变量值的操作,首先一种是替换操作符

${var_name:-word}:如果var_name的值存在,且非空,返回其值,否则返回word。

意图:如果变量未定义,返回一个默认值。

例子:${count:-0} 如果count未定义,则返回0。

${var_name:+word}:如果var_name存在且非空,返回word,否则返回null。

意图:测试一个变量的存在性。

例子:${count:+1} 如果count被定义了,则返回1。

${var_name:=word}:如果var_name存在且非空,返回其值,否则将其值设置为word,然后再返回其值。(对特殊参数跟位置参数无效)

意图:如果变量未定义,设置该变量为默认值。

例子:${count:=0} 如果count未定义,这是count为0。

${var_name:?message}:如果var_name存在且非空,返回其值;否则,打印“var_name:message”,并退出当前命令或者脚本。若省略message,则产生默认信息:parameter null or not set.

意图:捕获未定义的变量导致的错误。

例子:${count:?undefined!} 如果count为空,打印:“count:undefined!”

${var_name:offset:length}:用来切割var_name变量里面的内容,有点像java里面的String.subString(int start,int end)。从offset开始,切割length长度。

意图:切割字符串

例子:count=abcvoladorhaha ${count:3:7} 返回volador

下面看一下应用的例子:

存在这样的一个文件:其代表一个url跟该url被点击的次数。

10000 www.baidu.com 
 1000 www.a.com 
 100 www.b.com 
 1000000 www.oschina.net 
 ......

我们的工作是,写一个脚本,接受文件名(filename发)跟一个数字参数(num),效果是将文件中点击数排序,输出前num条记录。直接上代码:

highest.sh

#
# 	highest filename [howmany]
#     显示filename中前howmany条记录,howmany默认情况下是5
#
filename=${1}
filename=${filename:?"missing..."}
howmany=${2:-5}
sort -nr ${filename} | head -${howmany}
highest.sh

#
# 	highest filename [howmany]
#     显示filename中前howmany条记录,howmany默认情况下是5
#
filename=${1}
filename=${filename:?"missing..."}
howmany=${2:-5}
sort -nr ${filename} | head -${howmany}

还有一种字符操作叫做

模式和模式匹配


这家伙主要用一种模式来获取变量字符串的部分匹配内容,其实就是写正则表达式。下面看下怎么使用。

这里我们首先定义一个变量,以便下面的例子使用:file=/home/volador/book/long.file.name

操作符:${var_name#pattern} 如果模式匹配变量取值的开头(从开头开始匹配),则删除最的匹配部分,并返回其余部分。

例子:echo ${file#/*/}  结果:volador/book/long.file.name

例子:echo ${file#volador/*/}  结果:/home/volador/book/long.file.name (找不到从开头开始的匹配项)

操作符:${var_name##pattern} 如果模式匹配变量取值的开头(从开头开始匹配),则删除最的匹配部分,并返回其余部分。

例子:echo ${file##/*/} 结果:long.file.name

结尾(从尾部开始匹配),则删除最的匹配部分,并返回其余部分。

例子:${file%.*} 结果: /home/volador/book/long.file

操作符:${var_name%%pattern} 如果模式匹配变量取值的结尾(从尾部开始匹配),则删除最的匹配部分,并返回其余部分。

例子:${file%%.*} 结果:/home/volador/book/long

操作符:${var_name/pattern/string} ${var_name//pattern/string} 将var_name中受pattern匹配的最长部分替换成string的值。第一种格式中只有第一个匹配部分被替换;第二种格式中,所有匹配项都被替换。如果pattern以#开头,必须匹配var_name的开头部分。如果pattern以%开头,必须匹配var_name的结尾部分。如果string为空,则匹配部分被删除。

例子:${file/'/'/'-'} 结果:-home/volador/book/long.file.name

例子:${file//'/'/'-'} 结果:-home-volador-book-long.file.name

下面我们来做些实践吧。

1.写一个shell,将txt文件转换成gif文件 -_- 

shell: echo ${filename%.txt}.gif

2.写一个shell,参数是一个完整的文件路径,我们要从中抽取文件名。

shell: echo ${full_file_name##*/}

3.将PATH里面的内容分行打印出来。

shell: echo -e ${PATH//:/'\n'}

更多shell基础:http://asram.blog.51cto.com/1442164/440911