Shell数组
数组是一组数据的集合,数组中的每个数据被称为一个数组元素。目前Bash仅支持一维索引数组和关联数组,Bash对数组大小没有限制。
索引数组
定义索引数组的基本语法格式:
数组名[索引]=值
或
数组名=(值1值2值3...)
数组定义:
[root
localhostshell]#a[0]=11[root
localhostshell]#a[1]=22[root
localhostshell]#a[2]="Hello"[root
localhostshell]#a[3]="World"[root
localhostshell]#b=("Shell""Python""PHP")调用数组
${数组名[索引]}:返回一个数组元素
${数组名[*]}:将数组的所有元素作为一个整体全部返回
${数组名[
]}:将数组的所有元素视为独立的个体全部返回${#数组名[*]}或${#数组名[
]}:返回数组元素的个数${!数组名[*]}或${!数组名[
]}:返回数组的全部索引数组的调用:
[root
localhostshell]#echo${a[0]}11
[root
localhostshell]#echo${a[2]}Hello
[root
localhostshell]#echo${a[*]}HelloWorld
[root
localhostshell]#echo${#a[*]}4
[root
localhostshell]#echo${!a[*]}因为${数组名[
]}将所有数组元素视为独立的个体,所以可以使用循环语句遍历出所有的数组元素。[root
localhostshell]#foriin"${a[]}"do
echo$i
done
11
22
Hello
World
使用”数组名=(值1值2...)”方式定义的数组与第一种方式效果是一样的,使用这样的方式定义的数组,系统默认使用以0位起始值的有序数字为索引。
[root
localhostshell]#foriin"${b[]}"do
echo$i
done
Shell
Python
PHP
[root
localhostshell]#echo${!b[*]}[root
localhostshell]#echo${b[*]}ShellPythonPHP
[root
localhostshell]#关联数组
Bash从4.0版本开始支持关联数组,使用关联数组,数组的下标可以是任意字符串。关联数组的索引要求具有唯一性。
定义关联数组的基本格式:
declare-A数组名
数组名[key]=value
或
declare-A数组名
数组名=([key1]=值1[key2]=值2...)
定义关联数组必须使用declare-A数组名声明一个关联数组,其他的语法与普通索引数组一致。需要注意的是,索引数组无法转换为关联数组。
关联数组定义:
[root
localhostshell]#declare-Aman[root
localhostshell]#man[name]=Tom[root
localhostshell]#man[age]=20[root
localhostshell]#man[addr]="beijing"[root
localhostshell]#man[phone]=[root
localhostshell]#declare-Awomen[root
localhostshell]#women=([name]=Lily[age]=18[addr]=shanghai[phone]=)调用读取关联数组:
[root
localhostshell]#echo${man[name]}Tom
[root
localhostshell]#echo${#man[*]}4
[root
localhostshell]#echo${!man[]}namephoneageaddr
[root
localhostshell]#echo${women[*]}Lily18shanghai
击(最多18字)[root
localhostshell]#foriin${!man[*]};doecho"$i:${man[$i]}";donename:Tom
phone:
age:20
addr:beijing
[root
localhostshell]#Subshell
通过当前Shell启动的一个新的子进程或子Shell被称为SubShell(子Shell)。子Shell会自动继承父Shell的很多环境,如变量、工作目录、文件描述符等,反之,子Shell中的环境仅在子Shell中有效,父Shell无法读取子Shell的环境。
使用分组命令符号()可以让命令在子Shell中运行,通过Shell变量BASH_SUBSHELL可以查看子Shell的数量,该变量的初始值为0,每启动一个字Shell该变量的值会自动加1。
[root
localhostsubshell]#vimsubshell01.sh#!/bin/bash
#功能描述:子Shell演示示例
#父Shell
hi="parentshell"
echo"+++++++++++++"
echo-e"\[31m+父Shell+\[0m"
echo"+++++++++++++"
echo"PWD=$PWD"
echo"bash_subshell=$BASH_SUBSHELL"
#通过()开启子Shell
(
sub_hi="subshell"
echo-e"\t+++++++++++++"
echo-e"\t\[33m+子Shell+\[0m"
echo-e"\t+++++++++++++"
echo-e"\tPWD=$PWD"
echo-e"\tbash_subshell=$BASH_SUBSHELL"
echo-e"\thi=$hi"
echo-e"\tsubhi=$sub_hi"
cd/opt;echo-e"\tPWD=$PWD"
)
#返回父Shell
echo"+++++++++++++++++"
echo"+返回父Shell+"
echo"+++++++++++++++++"
echo"PWD=$PWD"
echo"hi=$hi"
echo"sub_hi=$sub_hi"
echo"bash_subshell=$BASH_SUBSHELL"
[root
localhostsubshell]#chmod+xsubshell01.sh[root
localhostsubshell]#./subshell01.sh+++++++++++++
+父Shell+
+++++++++++++
PWD=/opt/shell/subshell
bash_subshell=0
+++++++++++++
+子Shell+
+++++++++++++
PWD=/opt/shell/subshell
bash_subshell=1
hi=parentshell
subhi=subshell
PWD=/opt
+++++++++++++++++
+返回父Shell+
+++++++++++++++++
PWD=/opt/shell/subshell
hi=parentshell
sub_hi=
bash_subshell=0
[root
localhostsubshell]#脚本执行流程如下图所示:
另外,除了使用()可以启动子Shell外,还可以使用(将命令放入后台执行)、
(管道符)启动子Shell。使用命令替换符$()也会产生新的子Shell,在Shell脚本中执行一个外部命令同样会启动新的子Shell。
如下示例中使用管道符开启子Shell:
[root
localhostsubshell]#vimsubshell02.sh#!/bin/bash
#功能描述:使用管道符开启子Shell对程序的影响
sum=0
df
grep^/
whilereadnametotalusedfreeother
do
echo"free=$free"
letsum+=free
echo"sum=$sum"
done
echo$sum
[root
localhostsubshell]#chmod+xsubshell02.sh#由于使用管道符
启动了子Shell,导致while循环中的所有命令都是在子Shell中执行的,sum在子Shell中进行的自加运算,在父Shell中无效,故最终输出的值为0。
[root
localhostsubshell]#./subshell02.shfree=
sum=
0
[root
localhostsubshell]#那么如何解决上述管道符启动子Shell对程序的影响呢?可以通过文件重定向的方式读取文件的方式,就不会启动子Shell。
[root
localhostsubshell]#dfgrep^/df.txt
[root
localhostsubshell]#vimsubshell03.sh#!/bin/bash
#使用文件重定向,解决管道符启动子Shell对程序的影响问题
sum=0
whilereadnametotalusedfreeother
do
letsum+=$free
donedf.txt
echo$sum
[root
localhostsubshell]#chmod+xsubshell03.sh[root
localhostsubshell]#./subshell03.sh[root
localhostsubshell]#在脚本中使用外部命令,包括加载其他脚本也都会开启一个字Shell,所以在脚本中需要调用其他脚本时一定要使用source或.加载。
定义在其他脚本中使用的公共变量
[root
localhostsubshell]#vimenv.sh#!/bin/bash
#定义公共变量在其他脚本中使用
file="/etc/passwd"
password="T-have-a-dream"
error_info="Pleasetryagainlater"
编写脚本执行外部命令或加载其他脚本
[root
localhostsubshell]#vimsubshell04.sh#!/bin/bash
#使用bash命令调用其他脚本
bashenv.sh
echo"password=$password"
echo"Error:$error_info"
#使用source调用其他脚本
sourceenv.sh
echo"password=$password"
echo"Error:$error_info"
#使用.调用其他脚本
.env.sh
echo"password=$password"
echo"Error:$error_info"
[root
localhostsubshell]#chmod+xsubshell04.sh[root
localhostsubshell]#./subshell04.shpassword=
Error:
password=T-have-a-dream
Error:Pleasetryagainlater
password=T-have-a-dream
Error:Pleasetryagainlater
启动进程的方式
Shell中执行命令创建进程的方式有:fork方式、exec方式、source方式。
fork方式
通常情况下在系统中通过相对路径或绝对路径执行一个命令时,都会由父进程开启一个子进程,当子进程结束后再返回父进程,这种行为过程就叫做fork。
当脚本中正常调用一个外部命令或其他脚本时,都会fork一个子进程,我们的命令会运行在这个字Shell中。
[root
localhostsubshell]#vimsubshell05.sh#!/bin/bash
#功能描述:fork子进程的示例
#调用外部命令时会fork子进程
sleep10
#绝对路径或相对路径调用外部脚本时会fork子进程
/opt/shell/subshell/subshell02.sh
./subshell02.sh
[root
localhostsubshell]#chmod+xsubshell05.sh[root
localhostsubshell]#./subshell05.shfree=
sum=
0
free=
sum=
0
在执行上述脚本的同时时,新开一个窗口执行pstree命令查看进程树。
脚本执行结束后返回父进程。
exec方式
使用exec方式调用其他命令或脚本时,系统不会开启子进程,而是使用新的程序替换当前的Shell环境,因为当前Shell环境被替换了,所以当exec调用的程序结束后,当前环境会被关闭。
exec执行命令流程图:
[root
localhostsubshell]#vimexec.sh#!/bin/bash
#使用exec方式调用外部命令
execls
echo"test"
cd/etc;echo$PWD
[root
localhostsubshell]#chmod+xexec.sh[root
localhostsubshell]#./exec.shdf.txt env.sh exec.shsubshell01.sh subshell02.shsubshell03.shsubshell04.shsubshell05.sh
[root
localhostsubshell]#source方式
使用source命令或.(点)可以不开启子Shell,而在当前Shell环境中将需要执行的命令加载进来,执行完加载的命令后,继续执行脚本中后续的指令。
[root
localhostsubshell]#vimsource.sh#!/bin/bash
#使用source加载外部脚本
sourceenv.sh
echo"Error:$error_info"
ls/
[root
localhostsubshell]#chmod+xsource.sh[root
localhostsubshell]#./source.shError:Pleasetryagainlater
binbootdatadevdvdetc homeliblib64mediamntoptprocrootrunsbinsrvsystmpusrvar
[root
localhostsubshell]#