shell浅谈之十一别名、列表及数组

一、简介

       Shell中别名可以对命令进行重新命名,方便用户记忆长命名和定制自己熟悉的工作环境;列表是一组命名以逻辑与、逻辑或的关系串在一起,实现脚本程序的逻辑控制;数组是一重点,涉及数组的赋值、操作和字符串的处理,以及利用数组实现堆栈和二维数组等数据结构的存储。

二、详解

1、别名

       对linux命令重新命名,主要考虑到很多linux命令带有冗长的选项和参数,频繁使用这些长命令极易造成用户使用上的不便。别名的命令关键字是alias,基本格式为alias name='original-command'(等号两边不能有空格)。如alias ipconfig=ifconfig。若要删除已经设置的别名,使用内建命令unalias,unalias -a表示删除所有已设置的别名,unalias alias-name表示仅删除alias-name别名。

#!/bin/bash

shopt -s expand_aliases   #打开expand_aliases选项
alias detail="ls -l"
detail /root/in*          #别名支持通配符*
echo
directory=/root/
prefix=in*
alias vardetail="ls -l $directory$prefix"   #双引号和单引号在处理空格时是等价的,
                                            #但设置vardetail引用了变量,此处只能使用双引号
vardetail

echo "Deleting all aliases:"
unalias -a                #删除所有的别名
detail
vardetail
      若要在脚本中使用别名功能,必须在脚本使用shopt -s expand_aliases命令打开 expand_aliases(表示别名可以被扩展)选项,未打开,alias命令建立的别名不会执行。

      要注意的是:在诸如if/then结构、循环和函数等混合型结构中不能使用alias命令设置别名,但可以执行在混合型结构之外所设置的别名。

2、列表

(1)shell中列表是由一串命令用与运算(&&)和或运算(||)连接而成,分别称为与列表和或列表。与列表的格式为:命令1 &&命令2&& 命令3 &&... &&命令n,从左到右依次执行命名,直到某命令返回FALSE时(退出状态为非0)与列表执行终止。或列表的格式为:命令1 || 命令2 || 命令3 ||... ||命令n,从左到右依次执行命名,但是当某命令返回TRUE时(退出状态为0)或列表执行终止。

(2)与列表控制输入参数个数和返回状态码

#!/bin/bash

MAXARGS=3
ERROR=68
test $# -ne $MAXARGS  && echo "Usage: `basename $0` $MAXARGS arguments" && exit $ERROR
echo "Less than $MAXARGS arguments are passed to this script."
exit 0
shell浅谈之十一别名、列表及数组_第1张图片

(3)或列表控制输入参数个数和返回状态码

#!/bin/bash

MAXARGS=3
ERROR=68
test $# -eq $MAXARGS  || (echo "Usage: `basename $0` $MAXARGS arguments" && false) || exit $ERROR
echo "Correct arguments are passed to this script."
exit 0
      可以对比或列表和与列表的语句,由于echo命令总是返回TRUE,要将echo和false进行与运算从而返回FALSE值。其中test命令实际上是或列表的嵌套,在使用与或列表嵌套时需要利用圆括号区分逻辑运算的优先级。

3、数组

 (1)赋值和取值

       数组(Array)是一个由若干同类型变量组成的集合,数组均由连续的存储单元组成,最低地址对应于数组的第一个元素,最高地址对应于最后一个元素。Shell只支持一维数组,array[x]表示array数组的第x+1个元素,支持的最大数值标号是599 147 937 791。shell取得数组值(引用数组元素)格式是:${array[x]}

#!/bin/bash

city[0]=Nanjing      #对第1、2个数组元素赋值
city[1]=Beijing
city[9]=Melbourne    #对第10个数组元素赋值
city[15]=NewYork     #对第16个数组元素赋值

echo "city[0]=${city[0]}"
echo "city[1]=${city[1]}"
echo "city[9]=${city[9]}"
echo "city[15]=${city[15]}"
echo "city[2]=${city[2]}"    #未初始化数组的值为空
echo "city[10]=${city[10]}"
shell浅谈之十一别名、列表及数组_第2张图片

Shell中允许数组空缺元素,即可以不连续地给数组赋值。还可以用圆括号将一组值赋给数组。

#!/bin/bash

city=(Nanjing Beijing Melbourne NewYork)    #圆括号内以空格为分隔符,依次赋给数组元素

echo "city[0]=${city[0]}"
echo "city[1]=${city[1]}"
echo "city[2]=${city[2]}"
echo "city[3]=${city[3]}"
echo "city[4]=${city[4]}"       #未被初始化输出空
echo "city[5]=${city[5]}"
#!/bin/bash

city=(Nanjing [10]=Atlanta Massachusetts Marseilles)  #指定所赋元素的标号,并以此标号为起点继续赋值

echo "city[0]=${city[0]}"       #city[0]=Nanjing
echo "city[1]=${city[1]}"       #city[1]=
echo "city[10]=${city[10]}"     #city[10]=Atlanta
echo "city[11]=${city[11]}"     #city[11]=Massachusetts
echo "city[12]=${city[12]}"     #city[12]=Marseilles
echo "city[13]=${city[13]}"     #city[13]=
既然圆括号内允许对数组指定元素进行赋值,那么完全可以按照任意顺序指定任意元素对数组赋值。例如:

city=([2]=Nanjing [10]=Atlanta [1]=Massachusetts [5]=Marseilles)
(2)位置参数中$@和$*都表示传递到脚本的所有参数,在数组中@和*的应用是打印数组的所有元素。

#!/bin/bash

city=(Nanjing Beijing Melbourne NewYork)

for i in ${city[@]}  #等价于  for i in ${city[*]}
do                   #打印数组的所有元素
echo $i
done
#!/bin/bash

city[1]="Hong Kong"       #用引号包含空格的字符串
city[100]=Massachusetts
city[101]="New York"
city[10000]=Atlanta

for i in "${city[@]}"
do
echo $i
done
      由于city数组元素包含了空格,因此需要使用引号将${city[@]}引起来,for循环和@只打印被赋值的元素而不打印未赋值的元素。

      注意:"${city[@]}"将数组的所有元素分行打印,而"${city[*]}"只能将数组的所有元素打印在一行内且中间以IFS分隔。不用引号时${city[@]}和${city[*]}是等价的,使用引号时,@和*只是打印数组的方式存在差异。
(3)数组的用法

       数组的字符串操作与字符串操作的符号和意义完全一致,数组字符串操作特殊之处在于所有的操作都是针对所有的数组元素逐个进行的。

#数组的字符串操作
#!/bin/bash

city=(Nanjing Atlanta Massachusetts MaseillesMas)

echo "Extracting Substring"  #抽取字串
echo ${city[*]:0}            #抽取整个数组  Nanjing Atlanta Massachusetts Marseilles
echo ${city[*]:1}            #抽取从第1个元素到结束的数组  Atlanta Massachusetts Marseilles
echo ${city[*]:3}            #抽取从第3个元素到结束的数组  Marseilles
echo ${city[*]:0:2}          #抽取从第0个元素开始的两个元素  Nanjing Atlanta
echo
echo "Removing Substring"    #删除字串
echo ${city[*]#M*a}          #删除匹配M*a的最短字串  Nanjing Atlanta ssachusetts rseilles
echo ${city[*]##M*a}         #删除匹配M*a的最长字串  Nanjing Atlanta chusetts rseilles
echo
echo "Replcing Substring"    #替换子串功能
echo ${city[*]/M*s/Year}     #替换第1次与M*a匹配的子串  Nanjing Atlanta Year Year
echo ${city[*]//Mas/Year}    #替换所有与Mas匹配的子串   Nanjing Atlanta Yearsachusetts YeareillesYear
                             #替换的正则为最长匹配
      数组也可以存放read命令所读入的用户输入参数,declare -a city(并不是必须的)将city申明为数组,用read -a city命令将输入存储到city数组之中,输入以空格分开。可以使用unset city[0]命令清空一个元素,和unset city清空整个数组。使用${#city[@]}获取数组的长度。

      shell数组的一种重要操作:数组连接。

#!/bin/bash

city=(Beijing Nanjing Shanghai)
person=(Cai [5]=Wu Tang)             #数组不连续地赋值

declare -a combine                   #声明combine数组
combine=(${city[@]} ${person[@]})    #combine是数组city和person的连接,连接操作将不连续的元素按序存放到combine数组中

element_count=${#combine[@]}         #while循环输出combine数组
index=0
while [ "$index" -lt "$element_count" ]
do
   echo "Element[$index]=${combine[$index]}"  #数组标号与值的对应关系
   let "index=$index+1"
done
###################
echo
unset combine                                 #清空combine数组
combine[0]=${city[@]}                         #将city数组赋给combine[0]的一个元素
combine[1]=${person[@]}                       #将person数组赋给combine[1]的一个元素
element_count=${#combine[@]}
index=0
while [ "$index" -lt "$element_count" ]
do
   echo "Element[$index]=${combine[$index]}"
   let "index=$index+1"
done
###################
echo
declare -a subcombine=${combine[1]}          #声明数组时赋值
element_count=${#subcombine[@]}
index=0
while [ "$index" -lt "$element_count" ]
do
   echo "Element[$index]=${subcombine[$index]}"
   let "index=$index+1"
done
shell浅谈之十一别名、列表及数组_第3张图片

(4)数组实现简单的数据结构

      数据结构是指相互之间存在一种或多种特定关系的数据元素的集合,它直接影响到程序的运行速度和存储效率。Shell不直接支持如堆栈、队列、链表等数据结构,但可以很容易实现线性数据结构。对于树形、图等复杂的数据结构,shell理论上可以实现,但难度角度。

      利用数组实现堆栈操作。push函数将字符串压入堆栈,pop函数能弹出栈顶元素,status函数打印当前堆栈的状态信息。其中shift命令完成两个功能:第一,所有位置参数左移1位,即$2移动到$1的位置,$3移动到$2的位置,依次类推($0不变);第二,$#变量值减1。

#!/bin/bash

MAXTOP=50               #堆栈能存放元素的最大值

TOP=$MAXTOP             #栈顶指针,初始值是$MAXTOP

TEMP=
declare -a STACK        #全局数组STACK

push()                  #push进栈操作,可同时将多个元素压入堆栈
{
		if [ -z "$1" ]      #无参数,返回
		then
			 return
		fi

		#for ((i=1; i<=$#; i++))
		until [ $# -eq 0 ]   #until循环将push函数的所有参数都压入堆栈
		do
				let TOP=TOP-1    #栈顶指针减1

				STACK[$TOP]=$1
				shift            #脚本参数除$0外左移1位,$#参数总个数减1
		done

		return
}

pop()                     #pop出栈操作,执行pop函数使栈顶元素出栈
{
		TEMP=

		if [ "$TOP" -eq "$MAXTOP" ]   #若堆栈为空,返回
		then
		 return
		fi

		TEMP=${STACK[$TOP]}           #栈顶元素出栈
		unset STACK[$TOP]         
		let TOP=TOP+1                 #栈顶指针加1
		return
}

status()                          #显示当前堆栈内的元素,以及TOP指针和TEMP变量
{
		echo "==========STACK=========="
		for i in ${STACK[@]}                                
		do
		echo $i
		done
		echo "Stack Pointer=$TOP"
		echo "Just popped \""$TEMP"\" off the stack"
		echo "=========================="
}

push one              #1个元素入栈
status                #显示状态
push two three four   #3个元素入栈
status

pop                   #出栈
pop
status
push five six
status
shell浅谈之十一别名、列表及数组_第4张图片

     利用一维数组模拟实现二维数组,二维数组仍存储在一维数组中,只是通过行号和列号计算出数组的索引而已。可以采用该方法使shell脚本语言用于定义二维数组和矩阵。

#创建一个二维数组,并以逐行打印及旋转45度打印的两种方式将它打印出来
#!/bin/bash

#定义行数、列数,及数组名
ROW=5
COL=5
declare -a MATRIX      #申明MATRIX数组
 
load_alpha ()
{
		local rc=0
		local index
		
		#for循环将A-Y这25个字符存储到MATRIX数组
		for i in A B C D E F G H I J K L M N O P Q R S T U V W X Y
		do
				local row=`expr $rc / $COL`
				local column=`expr $rc % $ROW`
				let "index = $row * $ROW + $column"
				MATRIX[$index]=$i
				let "rc += 1"
		done  
}
  
print_alpha ()
{
		local row=0
		local index
		
		#逐行打印MATRIX数组
		while [ "$row" -lt "$ROW" ] 
		do                           
				local column=0
				echo -n "       " 
				while [ "$column" -lt "$COL" ]
				do
						let "index = $row * $ROW + $column"
						echo -n "${MATRIX[index]} " 
						let "column += 1"
				done
				let "row += 1"
				echo
		done
	  echo
}
  
filter ()     # 过滤掉负的数组下标. 
{
		echo -n "  "  # 产生倾斜. 
		              # 解释一下, 这是怎么做到的. 
		
		if [[ "$1" -ge 0 &&  "$1" -lt "$ROW" && "$2" -ge 0 && "$2" -lt "$COL" ]]
		then
		    let "index = $1 * $ROW + $2"
		    # 现在, 按照旋转方向进行打印. 
		    echo -n " ${MATRIX[index]}"
		    # alpha[$row][$column]
		fi    
}
 
rotate ()  # 将数组旋转45度 --
{          #+ 从左下角进行"平衡". 
		local row
		local column
		
		for (( row = ROW; row > -ROW; row-- ))
		  do  
		  for (( column = 0; column < COL; column++ ))
		  do
				  if [ "$row" -ge 0 ]
				  then
				    let "t1 = $column - $row"
				    let "t2 = $column"
				  else
				    let "t1 = $column"
				    let "t2 = $column + $row"
				  fi  
				  filter $t1 $t2   # 将负的数组下标过滤出来
		 done
	 
		echo; echo
		done 
}
 
 #下面利用上述函数创建二维数组,逐行和旋转45°打印数组
 load_alpha     # 加载数组
 print_alpha    # 打印数组
 rotate         # 逆时针旋转45°打印
shell浅谈之十一别名、列表及数组_第5张图片

三、总结

(1)别名、列表及数组这三个知识点是相对独立的,可分开理解。

(2)数组是重点,会使用数组字符串的操作,以及利用数组实现堆栈和二维数组等数据结构。

你可能感兴趣的:(Shell基础)