第三章、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脚本的作用域则是通过是否可以跨进程来区分。分为了自定义变量(局部变量)和环境变量(全局变量)。

  1. 自定义变量(局部变量)
    子进程不能访问的变量
  2. 环境变量(全局变量)
    子进程可以访问的变量

bash脚本提供了局部变量和全局变量的相互转化。

  1. 自定义变量 -> 全局变量
    主要有两种方法 export local_varialbe 或者是 declare -x local_variable
name=lucky-light
export name        # 第一种方法
declare -x name    # 第二种方法
  1. 全局变量 -> 自定义变量
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(不可嵌套

注意点:

  1. stdout 和 exit code 的区别
    stdout 就好像是 c++中的 printf操作,而 exit code 是函数 return 的数值。因此 $?$(command) 有不同的用处。
  2. $*$@
    两者的区别不大,都是将位置参数取出并展示,极个别情况下才会出现不同。
  3. $(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

4、参考链接

强烈推荐 AcWing的 Linux 基础课

Shell(Bash)单引号、双引号和反引号用法详解

posted @ 2022-06-26 17:34  lucky_light  阅读(110)  评论(0编辑  收藏  举报