第三章、shell变量和数组
第三章、shell变量和数组
0、总结
将总结放在前面是方便复习。
# 变量取值
echo ${name}acwing
# 只读变量
declare -r constant_variable_name
readonly constant_variable_name
# 局部变量->全局变量
export global_variable_name
declare -x global_variable_name
export PATH=$PATH:/home/xyg/anaconda/bin
# 全局变量->局部变量
declare +x name # 将全局变量转换为局部变量
# 删除变量
unset var_name
# 字符串
echo "hello, $name \"hh\""
echo ${#name} # 输出字符串长度 11
echo ${name:3:5} # 输出字符串name下标从3开始,长度为5的子串 -> ky-li
# 位置参数
echo "file_name $0"
echo "position 1: $1"
echo "position 2: $2"
# 数组
names=(xyg 'lucky-light', "I love acwing", `ls`)
names[0]=xyg
names[1]='lucky-light'
names[2]="I love acwing"
echo ${names[0]}
# 输出整个数组和他的长度
echo ${names[@]}
echo ${names[*]}
echo ${#names[@]}
echo ${#names[*]}
参数 | 含义 |
---|---|
$# |
代表文件传入的参数个数,如上例中值为3 |
$* |
由所有参数构成的用空格隔开的字符串,如上例中值为"$1 $2 $3" |
$@ |
每个参数分别用双引号括起来的字符串,如上例中值为"$1" "$2" "$3" |
$$ |
脚本当前运行的进程ID |
$? |
上一条命令的退出状态(注意不是stdout,而是exit code)。0表示正常退出,其他值表示错误 |
$(command) |
返回command 这条命令的stdout(可嵌套) |
`command` | 返回command 这条命令的stdout(不可嵌套) |
1、变量
变量,指值可以变的量。是编程语言中最为重要的部分。因为 Shell
脚本所针对的是文件管理,所以定义、使用、删除等语法会略显不同(和C、Python相比)。
变量定义
bash
脚本中,变量的类型都为字符串类型的,普通变量主要存在三种定义方式,如下所示:
s1=xyg
s2='xyg'
s3="xyg"
使用变量
借助于字符$
,可以取出变量的数值,也可以借助{}
圈中变量名,帮助解释器识别变量边界。
#! /bin/bash
name=lucky-light
echo $name
echo ${name}
echo ${name}acwing
只读变量
只读变量,或者是常量,bash
中有两种定义方式,使用declare -r variable_name
或者是 readonly variable_name
。
#! /bin/bash
# define the constant
name=lucky-light
readonly name
name=xyg
报错信息: line 6: name: readonly variable
#! /bin/bash
# define the constant
name=lucky-light
declare -r name
name=xyg
报错信息: line 6: name: readonly variable
删除变量
在其他语言中,我们往往不需要删除变量,变量也存在自己的作用域和生命周期。bash
脚本中提供了删除变量的方法。需要关键字 unset
。
unset
变量之后,未定义的变量就是一个空字符串。
注意 readonly
只读变量不予许删除 unset: name: cannot unset: readonly variable。
#! /bin/bash
# define the constant
name=lucky-light
echo $name
unset name
echo $name # this will output a blank line because we unset the variable
变量类型
变量是存在作用域的,bash
脚本的作用域则是通过是否可以跨进程来区分。分为了自定义变量(局部变量)和环境变量(全局变量)。
- 自定义变量(局部变量)
子进程不能访问的变量 - 环境变量(全局变量)
子进程可以访问的变量
bash
脚本提供了局部变量和全局变量的相互转化。
- 自定义变量 -> 全局变量
主要有两种方法export local_varialbe
或者是declare -x local_variable
name=lucky-light
export name # 第一种方法
declare -x name # 第二种方法
- 全局变量 -> 自定义变量
export name=lucky-light # 定义全局变量
declare +x name # 将全局变量转换为局部变量
为了验证结果,可以 bash
查看子进程内容。或者是我们调用其他脚本开的也是一个新的进程。
#! /bin/bash
# define the constant
name=lucky-light
echo $name
unset name
echo $name # this will output a blank line because we unset the variable
echo $name2 # to see the global variable can be get
结果如下:
字符串
字符串是 bash 脚本中最基本的数据,所有的数据存储都默认为字符串存储。
字符串可以用单引号,也可以用双引号,也可以不用引号。
单引号与双引号的区别:
- 单引号中的内容会原样输出,不会执行、不会取变量;如
echo '${name}'
中${name}
只会原样输出,不会取出数值。 但是 echo -e指名的转义,还是会转义的 - 双引号中的内容可以执行、可以取变量;如
echo "${name}"
中${name}
会取出变量中存储的内容。
具体而言,单引号和双引号用于变量值出现空格时,比如 name=zhang san 这样执行就会出现问题,而必须用引号括起来,比如 name="zhang san"。
不过,引号有单引号和双引号之分,二者的主要区别在于,被单引号括起来的字符都是普通字符,就算特殊字符也不再有特殊含义;而被双引号括起来的字符中,"$"、"\"和反引号是拥有特殊含义的,"$"代表引用变量的值,而反引号代表引用命令。
name=lucky-light # 不用引号
echo 'hello, $name \"hh\"' # 单引号字符串,输出 hello, $name \"hh\"
echo "hello, $name \"hh\"" # 双引号字符串,输出 hello, lucky-light "hh"
输出结果
[vivo4@~/xyg/test]$./1.sh
hello, $name \"hh\"
hello, lucky-light "hh"
获取字符串长度: 使用${#name}
name='lucky-light'
echo ${#name} # 输出字符串长度 11
提取子串: 使用${name:start_idx:sub_len}
。对于源字符串而言,下标从0
开始
name='lucky-light'
echo ${name:3:5} # 输出字符串name下标从3开始,长度为5的子串 -> ky-li
2、默认变量
默认变量是bash
中已经给定,具备特殊用途的变量,有文件参数变量和其他相关变量。
文件参数变量
在执行 shell
脚本时,可以向脚本传递参数。$1
是第一个参数,$2
是第二个参数,以此类推。
特殊的,$0
是第0个参数,文件名(包含路径)。比如说 './test_bash.sh' 或者是 '/home/xyg/test/test_bash.sh',具体得看执行 bash
文件时候,第0个参数的时候,你用的什么。
在 test_bash.sh
,测试查看位置参数。
#! /bin/bash
echo "file_name $0"
echo "position 1: $1"
echo "position 2: $2"
echo "position 3: $3"
运行该脚本
chmod a+x test_bash.sh
./test_bash.sh xyg lucky-light acw
运行结果
file_name ./test_bash.sh
position 1: xyg
position 2: lucky-light
position 3: acw
其他参数相关变量
参数 | 含义 |
---|---|
$# |
代表文件传入的参数个数,如上例中值为3 |
$* |
由所有参数构成的用空格隔开的字符串,如上例中值为"$1 $2 $3" |
$@ |
每个参数分别用双引号括起来的字符串,如上例中值为"$1" "$2" "$3" |
$$ |
脚本当前运行的进程ID |
$? |
上一条命令的退出状态(注意不是stdout,而是exit code)。0表示正常退出,其他值表示错误 |
$(command) |
返回command 这条命令的stdout(可嵌套) |
`command` | 返回command 这条命令的stdout(不可嵌套) |
注意点:
- stdout 和 exit code 的区别
stdout 就好像是 c++中的 printf操作,而 exit code 是函数 return 的数值。因此$?
和$(command)
有不同的用处。 $*
和$@
两者的区别不大,都是将位置参数取出并展示,极个别情况下才会出现不同。$(command)
和command
都是bash
中非常常用的形式,注意$(command)
是可以嵌套的。
3、数组
为了方便循环操作大批数据,大多数语言都设有数组这一概念。bash
中数组中可以存放多个不同类型的值(其实都是字符串),只支持一维数组,初始化时不需要指明数组大小。
数组的下标从 0 开始
定义
数组用小括号表示,元素之间用空格隔开。
names=(xyg 'lucky-light', "I love acwing", `ls`)
也可以直接定义数组中某个元素的值:
names[0]=xyg
names[1]='lucky-light'
names[2]="I love acwing"
names[3]=`ls`
读取数值
${array_name[idx]}
组合获取 array_name
数组下标 idx
的数值。
names=(xyg 'lucky-light', "I love acwing", "`ls`")
echo ${names[0]}
echo ${names[1]}
echo ${names[2]}
echo ${names[3]}
注意,"ls
"是加单引号、双引号、和不加单引号双引号结果是完全不一样的。
'`ls`' # 加单引号
"`ls`" # 加双引号
`ls` # 不加引号
names[3]=`ls` # 单个赋值
# 单引号结果
xyg
lucky-light,
I love acwing,
`ls`
# 双引号结果
xyg
lucky-light,
I love acwing,
1.sh test.csv test_bash.sh
# 不加引号结果
xyg
lucky-light,
I love acwing,
1.sh
加单引号, ` 符号不会被解析。
加双引号 ,` 符号会被解析,并且 ls
指令的结果将会作为整体字符串输出,
不加引号,ls
指令的结果因为会间隔空格,names[3], names[4]
会依次获取数值 value
但是 单个赋值 时候, ls
所有的结果都会放在names[3]
中,等价于加入了双引号
整个数组
输出整个数组的语法。(当然,你也可以自己遍历)
${array_name[@]} # 第一种写法
${array_name[*]} # 第二种写法
#! /bin/bash
names=(lucky-light, 'acwing', "xyg", "`ls`")
echo ${names[@]}
echo ${names[*]}
输出结果,不同数组元素使用 逗号 分隔
lucky-light, acwing, xyg, 1.sh test.csv test_bash.sh
lucky-light, acwing, xyg, 1.sh test.csv test_bash.sh
数组长度
类似于字符串
${#array[@]} # 第一种写法
${#array[*]} # 第二种写法
#! /bin/bash
names=(lucky-light, 'acwing', "xyg", "`ls`")
echo ${names[@]}
echo ${names[*]}
echo ${#names[@]}
echo ${#names[*]}
echo '############### tmp ###############'
tmp_str=${names[@]}
echo ${tmp_str}
echo ${#tmp_str}
输出结果,最有趣的是将 tmp_str=${names[@]}
数组整体信息赋值给 字符串变量后,使用 #
计算长度,是不同的。
也就是说计算数组长度时候,调用的 api 完全和计算字符串长度 不同。
lucky-light, acwing, xyg, 1.sh test.csv test_bash.sh
lucky-light, acwing, xyg, 1.sh test.csv test_bash.sh
4
4
############### tmp ###############
lucky-light, acwing, xyg, 1.sh test.csv test_bash.sh
52
数组单独赋值
数组的定义是允许我们给数组单个元素跳跃赋值的,但是他的空间分配如何,间隔的元素又是否分配了空间呢?
--> bash 数组赋值是内存优化过的,未赋值便不给分配空间
#! /bin/bash
names[0]=xyg
names[1]='lucky-light'
names[2]="I love acwing"
names[1000]=`ls`
echo ${names[@]} # 输出全部数组
echo ${names[*]} # 输出全部数组
echo ${#names[@]} # 输出数组长度
echo ${#names[*]} # 输出数组长度
echo 'after echo names[3]'
echo names[3] # 测试一下
echo ${#names[@]} # 输出数组长度,发现不变
echo 'after names[3]=test'
names[3]=test
echo ${#names[@]} # 数组长度会 + 1
echo 'give the empty'
names[10]=''
echo ${#names[@]} # 数组长度会 + 1,即使给的是空串
输出结果,可以发现,解释 echo names[3]
之后,内核仍不会给这个未定义的变量分配内存,也不会算入计数,只有真正赋值才可以。
xyg lucky-light I love acwing 1.sh test.csv test_bash.sh
xyg lucky-light I love acwing 1.sh test.csv test_bash.sh
4
4
after echo names[3]
names[3]
4
after names[3]=test
5
give the empty
6