Shell编程--数组、subshell、进程启动方式(fork、source、exec)

一、数组

1、定义数组
1)一维数组定义格式一:

数组名[索引 1]=1
数组名[索引 2]=2
数组名[索引 n]=值 n

2)一维数组定义格式二:

数组名=(值1 值2 值3)

3)关联数组定义:先声明后使用,关联数组的索引可以使任意字符。

declare -A  数组名
数组名[key1]=值1
数组名[key2]=值2
或者
数组名=[key1]=值1 [key2]=值2... )

案例一:定义数组

[root@localhost jiaofan]# name[0]=jiao         
[root@localhost jiaofan]# name[1]=fan
[root@localhost jiaofan]# name[2]=jiaofan
[root@localhost jiaofan]# echo ${name[1]}      #<==查看数组
fan
[root@localhost jiaofan]# echo ${name[2]}
jiaofan
[root@localhost jiaofan]# echo ${name[@]}      #<==查看全部的数组
jiao fan jiaofan
[root@localhost jiaofan]# echo ${name[*]}      #<==查看全部的数组
jiao fan jiaofan
[root@localhost jiaofan]# name[2+2]=jiaofan1   #<==索引可以使用算数表达式
[root@localhost jiaofan]# echo ${name[4]}
jiaofan1

案例二:第二种定义方法

[root@localhost jiaofan]# array=(tom  jack  alice)
[root@localhost jiaofan]# array=(`cat  /etc/passwd`)  #<==把文件中的每一行作为一个元素赋值给array,(因为每一行都没有空格,所以就每一行作为一个元素赋值给array)
[root@localhost jiaofan]# array=(`ls /home`)  #<==以空格为分隔符
[root@localhost jiaofan]# arrary=($red  $blue  $green)    #<==可以把变量赋值给数组
[root@localhost jiaofan]# array=(1 2 3  "linux shell"  [20]=saltstack)     #<==可以直接赋值需要的索引

案例三:关联数组定义

[root@localhost jiaofan]# declare -A woman
[root@localhost jiaofan]# woman=([up]=lvbu [down]=daxiaojie  [contral]=ailin)
[root@localhost jiaofan]# echo ${woman[*]}
ailin lvbu daxiaojie
[root@localhost jiaofan]# declare -A man
[root@localhost jiaofan]# man[name]=Tom
[root@localhost jiaofan]# man[age]=24
[root@localhost jiaofan]# echo ${man[*]}
Tom 24

案例四:while循环读入数据遇到回车键结束

1)循环定义数组

While read  line   #<==读入一行赋值给line
do
   Hosts[++i]=$line
done < /etc/hosts

2)用循环遍历数组

for  i  in  ${!hosts[@]}
do
   echo$i${hosts[$i]}done

案例五:for循环读入数据遇到空格键就结束

1)循环定义数组

for line in `cat /etc/hosts`
do
	hosts[++i]=$line
done

2)用循环遍历数组

for i in ${!hosts[@]}
do
	echo "$i${hosts[$i]}"
done

案例六:统计性别

[root@localhost jiaofan]# cat sex.txt 
jack m
alice f
tom m
[root@localhost jiaofan]# vi sex-conut.sh
[root@localhost jiaofan]# sh sex-conut.sh 
f:1
m:2
[root@localhost jiaofan]# cat sex-conut.sh 
declare -A sex
while read line
do
	type=`echo $line | awk '{print $2}' `
	let sex[$type]++ 
done < sex.txt

for i in ${!sex[@]}
do
	echo "$i${sex[$i]}"
done

案例七:

#!/bin/bash
declare  -A  array
for i in  {1..1000}
do
	awk 'NR=='"$i"'{print $0}' ./num1 > num1-1 
	num1=`cat num1-1`
	awk 'NR=='"$i"'{print $0}' ./num2 > num2-2
	num2=`cat num2-2`
	array[$num1]=$((${array[$num1]}+${num2}))
done

for  i  in  ${!array[@]}
do
     echo "$i  ${array[$i]}"
done

2、查看数组

1)查看数组第一个值

[root@localhost jiaofan]# echo  ${array}
[root@localhost jiaofan]# echo  ${array[0]}

2)查看数组最后一个值

[root@localhost jiaofan]# name=(a b c d e f)
[root@localhost jiaofan]# echo ${name[-1]}       #<==查看倒数第一个元素的值
f
[root@localhost jiaofan]# echo ${name[-2]}       #<==查看倒数第二个元素的值
e

3)查看数组全部的值

[root@localhost jiaofan]# echo ${name[*]}
a b c d e f
[root@localhost jiaofan]# echo ${name[@]}
a b c d e f

4)查看数组任意一段值

[root@localhost jiaofan]# echo ${name[@]:2:3}   #<==从数组下标2开始,访问3个元素
c d e
[root@localhost jiaofan]# echo ${name[@]:2}     #<==从数组下标2开始,访问之后所有元素
c d e f

5)统计数组中的个数

[root@localhost jiaofan]# echo ${#name[@]}
6
[root@localhost jiaofan]# echo ${#name[*]}
6

6)查看索引

[root@localhost jiaofan]# echo ${!name[*]}
0 1 2 3 4 5

7)访问所有的数组

declare  -a

【注】for [ i in name ]:把name的索引给i
for [ i in $name ]:把name的值给i

3、修改数组

[root@localhost jiaofan]# name=(a b c d e f)
[root@localhost jiaofan]# echo ${name[*]}
a b c d e f
[root@localhost jiaofan]# name[2]=avc        #<==修改anme[2]
[root@localhost jiaofan]# echo ${name[*]}
a b avc d e f

4、删除数组

[root@localhost jiaofan]# name=(a b c d e f)
[root@localhost jiaofan]# echo ${name[*]}
a b c d e f
[root@localhost jiaofan]# unset name         #<==删除索引
[root@localhost jiaofan]# echo ${name[*]}

[root@localhost jiaofan]# 

案例一:name[@]和name[*]的区别

使用*提取数组中的所有元素时,会把所有元素视为一个整体,而使用@则将数组所有元素视为若干个体。

[root@localhost jiaofan]# bash arrry.sh
1-"a b c d e f"            #<==用到*时,abcdef是一个整体,所以循环一次就结束
[root@localhost jiaofan]# cat arrry.sh 
#!/bin/bash
name=(a b c d e f)
for i in "${name[*]}"
do  
	let y++
	echo $y-\"$i\"
done
[root@localhost jiaofan]# bash arrry.sh  #<==用到@时,abcdef以IFS的空格为分隔符,所以循环多次才结束
1-"a"
2-"b"
3-"c"
4-"d"
5-"e"
6-"f"
[root@localhost jiaofan]# cat arrry.sh 
#!/bin/bash
name=(a b c d e f)
for i in "${name[@]}"
do  
	let y++
	echo $y-\"$i\"
done

案例三:

[root@localhost jiaofan]# df / 
文件系统                   1K-块     已用     可用 已用% 挂载点
/dev/mapper/centos-root 58771456 25709740 33061716   44% /
[root@localhost jiaofan]# df / | tail -n +2
/dev/mapper/centos-root 58771456 25709740 33061716   44% /
[root@localhost jiaofan]# root=($(df / | tail -n +2))
[root@localhost jiaofan]# echo ${root[0]}
/dev/mapper/centos-root
[root@localhost jiaofan]# echo ${root[*]}
/dev/mapper/centos-root 58771456 25709740 33061716 44% /
[root@localhost jiaofan]# 

二、subshell

通过当前 Shell 启动的一个新的子进程或子 Shell 被称为 SubShell (子 Shell )。子 Shell 会自动继承父 Shell 的很多环境,如环境变量、工作目录、文件描述符等,但是反之,子 Shell 中的环境仅在子 Shell 中有效,父 Shell 无法读取子 Shell 的环境。例如,如果在父 shell 中定义全局变量,子 shell 中就可以调用该变量。但当在子 shell 中定义一个局部变量时,父 shell 是无法读取该变量的。

1)如何生成子shell ?

shell中有一个变量 BASH_SUBSHELL 可以查看子 shell 的信息,该变量的初始值为0,每启动一个子 shell 该变量就会自动加1。
由下面的案例可以看到bash_subshell在子进程中的值是1,可以确定()开启了子进程。

[root@localhost jiaofan]# ./subshell_01.sh 
我是父进程,我的当前路径是:/root/jiaofan,我的bash_subshell=0
./subshell_01.sh:行10: am: 未找到命令
我是子进程,我的当前路径:/root/jiaofan,我的bash_subshell=1
我是已经返回父进程,我的当前路径是:/root/jiaofan,我的bash_subshell=0
hi=hell0
sub_hi=
[root@localhost jiaofan]# chmod +x subshell_01.sh 
[root@localhost jiaofan]# ./subshell_01.sh 
我是父进程,我的当前路径是:/root/jiaofan,我的bash_subshell=0
我是子进程,我的当前路径:/root/jiaofan,我的bash_subshell=1
我是已经返回父进程,我的当前路径是:/root/jiaofan,我的bash_subshell=0
hi=hell0
sub_hi=
[root@localhost jiaofan]# cat subshell_01.sh 
#!/bin/bash
#描述:演示生成子shell
#子shell会继承父 shell 的环境变量,但父shell 无法读取子shell的环境。

hi="hell0"
echo "我是父进程,我的当前路径是:$PWD,我的bash_subshell=$BASH_SUBSHELL"

#开启子进程
(
	sub_hi="I am a subshell"
	echo "我是子进程,我的当前路径:$PWD,我的bash_subshell=$BASH_SUBSHELL"
)

echo "我是已经返回父进程,我的当前路径是:$PWD,我的bash_subshell=$BASH_SUBSHELL"

echo "hi=$hi"
echo "sub_hi=$sub_hi"
[root@localhost jiaofan]# 

2)父shell 不可以读取子shell的环境信息。

在下面案例中,子shell改变了工作目录和定义了一个变量。返回到父shell中去调用,发现调用无效。这就证明父shell 不可以读取子shell的环境信息。

[root@localhost jiaofan]# ./subshell_01.sh 
我是父进程,我的当前路径是:/root/jiaofan,我的bash_subshell=0
我是子进程,我的当前路径:/data,我的bash_subshell=1
我是已经返回父进程,我的当前路径是:/root/jiaofan,我的bash_subshell=0
hi=hell0
sub_hi=
[root@localhost jiaofan]# cat subshell_01.sh 
#!/bin/bash
#描述:演示生成子shell
#子shell会继承父 shell 的环境变量,但父shell 无法读取子shell的环境。

hi="hell0"
echo "我是父进程,我的当前路径是:$PWD,我的bash_subshell=$BASH_SUBSHELL"

#开启子进程
(
	sub_hi="I am a subshell"
	cd /data
	echo "我是子进程,我的当前路径:$PWD,我的bash_subshell=$BASH_SUBSHELL"
)

echo "我是已经返回父进程,我的当前路径是:$PWD,我的bash_subshell=$BASH_SUBSHELL"

echo "hi=$hi"
echo "sub_hi=$sub_hi"
[root@localhost jiaofan]# 

3)除了()还有其他可以开启子shell的吗?

比如&后台运行,使用管道命令 | 或者分组符号(),会用替换命令$()也会产生新的子shell。
从下面的案例中可以看出,sum已经算出了总值,但是输出依然是0。这是因为管道符 | 开启了子shell,所以让整个循环在子shell 中完成,父shell当然无法获取子shell的变量值。可以用return返回sum的值,这个后面有说。

[root@localhost jiaofan]# ./subshell_02.sh 
free=33061680
sum=33061680
free=898016
sum=33959696
0
[root@localhost jiaofan]# cat subshell_02.sh 
#!/bin/bash
#功能描述:使用管道开启子 shell 后导致运行错误的案例

sum=0
df | grep "^/" | while read name total used  free other
do 
	echo "free=$free"
	let sum+=free
	echo "sum=$sum"
done
echo $sum

4)除了return,返回父shell值外,还有重定向输入可以不用开启子shell,所以就不会涉及到父shell到子shell取变量值的问题。

[root@localhost jiaofan]# ./subshell_02.sh 
free=33061680
sum=33061680
free=898016
sum=33959696
33959696
[root@localhost jiaofan]# cat subshell_02.sh 
#!/bin/bash
#功能描述:使用管道开启子 shell 后导致运行错误的案例

tmp_file=/root/jiaofan/subshell-$$.txt

df | grep "^/" > $tmp_file
while read name total used  free other
do 
	echo "free=$free"
	let sum+=free
	echo "sum=$sum"
done < $tmp_file
rm -f $tmp_file
echo $sum
[root@localhost jiaofan]# 

5)在脚本中使用外部命令,包括加载其他脚本也都会开启一个子shell,所以在脚本中需要调用其他脚本时一定要使用source加载。source调用的./env.sh的bash_subshell依然是0。而且调用了env.sh 中的变量。

[root@localhost jiaofan]# ./subshell_5.sh 
我是父shell。我的bash_subshell=0
我是source的进程,我的bash_subshell=0
name1=jiaofan
name2=tom
[root@localhost jiaofan]# cat env.sh
#!/bin/bash

name1=jiaofan
name2=tom
echo "我是source的进程,我的bash_subshell=$BASH_SUBSHELL"
[root@localhost jiaofan]# cat subshell_5.sh 
#!/bin/bash
echo "我是父shell。我的bash_subshell=$BASH_SUBSHELL"

source ./env.sh
echo "name1=${name1}"
echo "name2=${name2}"
[root@localhost jiaofan]# 

三、进程启动方式

1、fork

  1. 在系统中使用相对路径或绝对路径执行一个命令。
  2. 正常调用外部命令或其他脚本。
[root@localhost ~]# cat subshell_6.sh 
#!/bin/bash
#功能描述:fork 子进程的示例

#调用外部命令时会fork子进程
sleep 5

#绝对路径或相对路径调用外部脚本时会fork子进程
/root/tmp.sh
cd /root;./tmp.sh

2、exec
使用exec调用其他命令或脚本,语法如下:

exec  [命令]  [参数]

1)如下脚本,exec调用ls命令,执行以后就结束了整个脚本。但是可以看出exec之前的命令正常被执行。

[root@localhost ~]# ./exec.sh 
开始
2  a  anaconda-ks.cfg  chess.sh  exec.sh  jiaofan  mysql  subshell_6.sh  test  test.txt  ver1.txt  ver2.txt
[root@localhost ~]# ls
2  a  anaconda-ks.cfg  chess.sh  exec.sh  jiaofan  mysql  subshell_6.sh  test  test.txt  ver1.txt  ver2.txt
[root@localhost ~]# 

2)上述情况有一个特例:当exec 后面的参数是文件重定向时,不会替换当前shell 环境,脚本后续的其他命令不会受任何影响。

3、source

使用source 命令或.(点)可以不开启子shell,而在当前shell 环境中将需要执行的命令加载进来,执行完加载命令后,继续执行脚本中的命令。

[root@localhost jiaofan]# ./subshell_5.sh 
我是父shell。我的bash_subshell=0
我是source的进程,我的bash_subshell=0
name1=jiaofan
name2=tom
[root@localhost jiaofan]# cat subshell_5.sh 
#!/bin/bash
echo "我是父shell。我的bash_subshell=$BASH_SUBSHELL"

source ./env.sh
echo "name1=${name1}"
echo "name2=${name2}"
[root@localhost jiaofan]# cat env.sh 
#!/bin/bash

name1=jiaofan
name2=tom
echo "我是source的进程,我的bash_subshell=$BASH_SUBSHELL"
[root@localhost jiaofan]# 

在执行./subshell_5.sh 的时候等于执行了如下命令,env.sh的脚本内容替换了./env.sh

#!/bin/bash
echo "我是父shell。我的bash_subshell=$BASH_SUBSHELL"

name1=jiaofan
name2=tom
echo "我是source的进程,我的bash_subshell=$BASH_SUBSHELL"
echo "name1=${name1}"
echo "name2=${name2}"
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值