参数和变量
变量的可见性
调用当前shell的shell是看不到当前shell的变量的,而如果调用当前shell的shell将它的变量export到环境中,则可以被当前shell看到。
export的方式为:
var=whatever
export var
这样就把var加入到的环境中,它可以被当前shell的所有子进程看到。
一个变量被export后,除非被unset,否则会一直在环境中。
printf "%s\n" ${RANDOM}{,,,,,} |
while read num
do
[ $num -gt ${biggest:=0} ] && biggest=$num
done
printf "The largest number is: %d\n" "$biggest"
输出将会是0,问题并不是在while循环,而是因为while循环是整个管道的一部分,而管道是被看成是子shell。
在前一章已经提到过,可以用命令代替来解决这个问题:
while read num
do
[ $num -gt ${biggest:=0} ] && biggest=$num
done < <(printf "%s\n" ${RANDOM}{,,,,,})
printf "The largest number is: %d\n" "$biggest"
这是因为在这种情况下,while循环仍然在当前shell中。
注意:在前一情况下,就算在当前shell中将biggest export到环境中也只能得到0,这个应该跟写时复制有些类似,当子进程对与父进程共有的变量进行改动时,该变量会复制一份到子进程,改动的被会是这个复制版本,父进程中该变量不会有改变。
shell变量
shell有80多个变量,其中有大约一半由shell自动赋值,其他的由操作系统、用户、终端和脚本设定。
前面已经介绍过的一些shell变量有:RAMDOM,PWD,OPTIND,OPTARG
等。
有时候BASH_VERSION(或BASH_VERSINFO)可以用来查看当前运行的shell能否运行该脚本。如:
case $BASH_VERSION in
[12].*) echo "You need at least bash3.0 to run this script" >&2; exit 2;;
esac
变量命名
传统上来说,环境变量应该用大写字母来命名,本地变量应该用小写字母来命名,但考虑到shell自带有80个变量,如果用大写字母来命名的话很容易造成冲突,所以一般大写字母命名的变量前面最好加一个下划线(因为shell变量都不是以下划线开头的)。
参数扩展
bourne shell
${var:-default}:如果var是unset或为空,则结果为default。
${var-default}:如果var是unset,则结果为default。
相反
${var:+alternate}:如果var不为空,则结果为alternate。
${var+alternate}:如果var是set,则结果为alternate。
以上都不改变var的值。
如:
var=
for n in a b c d e f g
do
var="${var:+"$var "}$n"
done
sa "$var"
输出将a,b,c,d,e,f,g以空格为分开,但a前面和g后面都没有空格。
${var:=default}与{var:-default}类似,只是会改变var的值。
类似有:
${var=default}
${var:?message}:如果var为空则输出message。
${var?message}:如果var是unset,则输出message。
POSIX shell
${#var}:结果为var的长度。
${var%PATTERN}:去掉var尾部与PATTERN匹配的最短串。(可以用这个获得上层目录的路径)
${var%%PATTERN}:去掉var尾部与PATTERN匹配的最长串。
${var#PATTERN}:去掉var头部与PATTERN匹配的最短串。
${var##PATTERN}:去掉var头部与ATTERN匹配的最长串。(比如说前面几章里提到的一个例子里有scriptname=${0##*/})
bash
${var//PATTERN/STRING}:将var中所有匹配PATTERN的子串换成STRING。
${var/PATTERN/STRING}:只替换第一个匹配的。
如:
passwd=zxQ1.=+-a
printf "%s\n" "${passwd/[[:punct:]]/*}"
输出将会是:
zxQ1*=+-a
([[:punct:]]匹配点)
${var:OFFSET:LENGTH}:结果为从OFFSET 开始长度为LENGTH的子串,OFFSET从0开始,LENGTH可以不指定,此时会输入从OFFSET到末尾的子串。
OFFSET为负数时则从字符串尾开始数。为了与前面提到的:-区分开,这时的负号前面要有空格。
${!var}:间接引用。
如:
x=yes
a=x
sa "${!a}"
则输出为
:yes:
这相当于命令:
eval "sa \$$a"
bash-4.0
${var^PATTERN}:如果第一个字符符合PATTERN,则将其改为大写。如果PATTERN为空,则认为匹配。
${var^^PATTERN}:var中任一字符如果符合PATTERN,则将其改为大写。
${var,PATTERN}
${var,,PATTERN}与前两个类似,只是改为小写。
位置参数
$0为脚本所在的路径。
shift:将参数左移,$2变为$1,$3变为$2等等。$0不变。
shift后可接参数使一次移动几位。
shift "$#"将会移掉所有的位置参数。
于是依次获取各个位置参数可以有以下两种方法:
for param in "$@" ## or just:
do
: do something with $param
done
或
while [ $# -gt 0 ] ## or: while [ -n "$*" ]
do
: do something with $1
shift
done
数组
bash是支持一维数组的(POSIX shell是不支持的),数组的下标为整数,从4.0开始也可以用字符串作为下标。
要获得数组中的第n项用${array[n]}。
${array[*]}会将array作为一个参数输出。
${array[@]会将array每个元素作为一个参数输出。(类似于$*和$@)
对数组中的第n项赋值则用array[n]=var即可。
${#array[@]}得到的是数组中元素个数。
还可以这样对数组赋值:
province=( Quebec Ontario Manitoba )
用+=对数组添加元素。
province+=( Alberta "British Columbia" "Nova Scotia" )
注意空格。
如果要用字符串作为数组的下标,则要进行声明:
declare -A array
${array["c"]}得到array中以c为下标的元素。