李守中

Bash 1 变量

Table of Contents

1. 基本变量

Bash 量没有数据类型的概念,所有变量类型和值类型都是字符串。

1.1. 创建变量

变量命名规则:

  1. 由字母、数字和下划线组成。
  2. 第一个字符不能是数字。
  3. 不能用标点和空格。

创建方法:

# 等号两边无空格,赋值
variable=value
# 等号两边有空格,判断
var1 = var2

# 变量值有空格,要用 "" 包起来
variable="value_part1 value_part2"

创建变量的例子:

# 例子
a=z                     # 变量 a 赋值为字符串 z
b="a string"            # 变量值包含空格,必须放在引号内
c="a string and $b"     # 变量值内可以引用其他变量的值
d="\t\ta string\n"      # 变量值内可以使用转义字符
e=$(ls -l foo.txt)      # 变量值可以是命令的执行结果
f=$((5 * 7))            # 变量值可以是数学运算的结果

1.2. 读取变量

1.2.1. 读取变量值

在变量名前加 $ 即可。

Bash 每遇到 $ 就会将其后的一个单词作为变量名并读取其值,若该变量名对应的变量不荐在,则输出空字符。

$ 在 Bash 里有特殊的含义,所以在作为美元符号时要小心:

# 命令本意是输出 $100
# 但 Bash 会将 $1 解释为变量,而 $1 为空,按空字符串处理
# $100 就变成了 00
$ echo The total is $100.00
The total is 00.00
# 若要使用 $ 原义,需要写成 \$
$ echo The total is \$100.00
The total is 100.00

当变量名与其它字符连用时,需要用 {} 包围变量名:

$ a=foo
$ echo $a_file

$ echo ${a}_file
foo_file

如果值本身也是变量,使用 ${!varname} 可以读取最终的值 (! 表示只向下解析一层):

$ var=USER
$ echo ${!var}
lsz

如果变量值包含连续空格、制表符或换行符,最好放在双引号里面读取:

$ a="1 2  3"
$ echo $a
1 2 3
$ echo "$a"
1 2  3

1.2.2. 读取变量长度

${#var} 获取 var 变量字符串化后的长度。

$ var=123
$ echo ${#var}
3
$ var="a b c"
$ echo ${#var}
5

1.3. 修改变量值

变量可以重复赋值,后面的赋值会覆盖前面的赋值:

$ foo=1
$ echo $foo;foo=2 # 一行内有多个语句,必须用 ; 分隔
1
$ echo $foo
2

1.4. 删除基本变量

unset 命令用来删除变量。

unset <var_name>

它不是很有用。因为 不存在的 Bash 变量一律按空字符串处理 ,所以变量被删除后,还是可以被读取为空字符串。

所以, 要删除一个变量,也可以将这个变量设成空字符串

# 删除了变量 foo 的 2 种写法
foo=''
foo=

2. 数组变量

Bash 只支持一维数组 (one-dimensional indexed array),不支持高维数组。

2.1. 创建数组

# 逐个赋值
arr[0]=val0
arr[1]=val1
arr[2]=val2
 # 一次性赋值
 arr=(val1 val2 val3 ... valn)
 # 等同于
 arr=(
     val1
     val2
     val3
     ...
     valn
)
# 指定位置赋值
arr=(a b c)
# 等同于
arr=([2]=c [0]=a [1]=b)
# 又比如
days=(Sun Mon Tue Wed Thu Fri Sat)
days=([0]=Sun [1]=Mon [2]=Tue [3]=Wed [4]=Thu [5]=Fri [6]=Sat)

# 只为某些位置赋值,其它留空
# 比如,hatter 是 0 号位,duchess 是 5 号位,alice 是 6 号位
names=(hatter [5]=duchess alice)
# 通配符赋值
# 将当前目录的所有 MP3 文件,放进一个数组
$ mp3s=(*.mp3)
# 将用户的命令行输入,存入一个数组
$ read -a dice
la lb lc ld     # 键盘输入
$ declare -p dice
declare -a dice=([0]="la" [1]="lb" [2]="lc" [3]="ld")

2.2. 读取数组

2.2.1. 读取单个元素

${array[i]} 可获取单个值, i 是索引。

$ array[0]=a
$ echo ${array[0]}
a

# 大括号不可少,否则 Bash 会把 [i] 按原样输出
$ echo $array[0]
a[0]

数组名 arr 默认对应 arr[0]:

$ declare -a arr
$ arr=A
$ echo ${arr[0]}
A
$ arr=(1 2 3)
$ echo ${arr}
1
$ echo $arr
1

2.2.2. 读取所有元素

# 读取所有值
$ arr=(a b c d e f)
$ echo ${arr[@]}
a b c d e f
# 遍历数组
for item in "${arr[@]}"; do
    echo $item
done

# "${names[@]}" 的 "" 一定要加上,否则
$ activities=(1 "2 3" 4 "5 6" 7)
$ for act in ${activities[@]}; \
do \
    echo "Activity: $act"; \
done
Activity: 1
Activity: 2
Activity: 3
Activity: 4
Activity: 5
Activity: 6
Activity: 7
# ${arr[@]} 和 ${arr[*]} 有区别
# ${arr[@]} 把数组的每一个元素都创建为一个参数
$ arr=(1 2 3)
$ for item in "${arr[@]}"; \
do \
    echo "example.$item" \
done
example.1
example.2
example.3

# ${arr[*]} 只创建了一个参数,即数组整体
$ for item in "${arr[*]}"; \
do \
    echo "example.$item" \
done
example.1 2 3

2.2.3. 读取某段元素

${array[@]:position:length} 命令可以提取数组元素。

$ food=(apples bananas cucumbers dates eggs fajitas grapes)
$ echo ${food[@]:1:1}
bananas
$ echo ${food[@]:1:3}
bananas cucumbers dates

2.2.4. 获取元素数量

${#var[@]}${#var[*]} 获取数组 var 中的元素个数。

$ var=("a233333" "b" "c")
$ echo ${#var[@]}
3

2.2.5. 获取元素索引

${!arr[@]}${!arr[*]} ,可以返回数组的元素序号,即哪些位置是有值的:

$ arr=([5]=a [9]=b [23]=c)
$ echo ${!arr[@]}
5 9 23
$ echo ${!arr[*]}
5 9 23

利用这个方法,可以通过 for 循环遍历数组:

arr=(a b c d)

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

2.3. 修改数组

2.3.1. 数组拷贝

# 数组拷贝
$ hobbies=("${arr[@]}")
# 可以为新数组添加元素
$ hobbies=("${arr[@]}" 10 11)

2.3.2. 追加数组元素

+= 运算符能够把值追加到数组末尾:

$ arr=(a b c)
$ echo ${arr[@]}
a b c

$ arr+=(d e f)
$ echo ${arr[@]}
a b c d e f

2.4. 删除数组元素与数组

2.4.1. 删除数组元素与数组

unset 命令可以删除一个数组元素或数组。

$ arr=(a b c d e f)
$ echo ${arr[@]}
a b c d e f
$ unset arr[2]
$ echo ${arr[@]}
a b d e f
$ unset arr
$ echo ${arr[@]}
<--no output-->

2.4.2. 隐藏数组元素

将某个元素设为空值,可以从返回值中隐藏这个元素。

$ arr=(a b c d e f)
$ arr[1]=''
$ echo ${arr[@]}
a c d e f

注意, 这里是隐藏,而不是删除,因为这个元素仍然存在,只是值变成了空值。

$ foo=(a b c d e f)
$ foo[1]=''
$ echo ${#foo[@]}
6
$ echo ${!foo[@]}
0 1 2 3 4 5

2.5. 关联数组

关联数组使用字符串而不是整数作为数组索引,且必须用 declare -A <array_name> 创建。

declare -A colors
colors["red"]="#ff0000"
colors["green"]="#00ff00"
colors["blue"]="#0000ff"

关联数组的操作方式,几乎与整数索引数组相同。

3. 特殊变量

3.1. 基础特殊变量

$?: 上一个命令的退出码。返回值是 0,表示上一个命令执行成功;不是 0,表示上一个命令执行失败。

$ ls doesnotexist
ls: doesnotexist: No such file or directory

$ echo $?
1

$$: 当前 Shell 的进程 ID。

$ echo $$
10662
# 可以用来命名临时文件
LOGFILE=/tmp/output_log.$$

$_: 为上一个命令的最后一个参数。

$ grep dictionary /usr/share/dict/words
dictionary

$ echo $_
/usr/share/dict/words

$!: 最近一个后台执行的异步命令的进程 ID。

$ firefox &
[1] 11064

$ echo $!
11064

$0: 在命令行直接执行时是当前 Shell 的名称。在脚本中执行时是脚本名。

$ echo $0
bash

$-: 当前 Shell 的启动参数。

$ echo $-
himBHs

$@: 脚本的所有参数值。

$#:脚本的参数数量。

3.2. 创建特殊变量

declare 命令可以声明一些特殊类型的变量,为变量增加一些限制:

declare option variable=value

declare 命令如果用在函数中,声明的变量只在函数内部有效,等同于 local 命令。

declare 命令不带任何参数时,输出当前环境的所有变量,包括函数在内,等同于不带有任何参数的 set 命令。

declare 命令的主要参数如下:

  • -a 声明数组变量。声明后变量可以按数组处理。
  • -A 声明关联数组变量。声明后变量可以关联按数组处理。
  • -f 输出所有函数的定义。
  • -F 输出所有函数名。
  • -i 声明整数变量。
  • -l 声明变量为小写字母。
  • -p 查看变量信息。对于未定义的变量,会提示找不到。如果不提供变量名, declare -p 输出所有变量的信息。
  • -r 声明只读变量。创建后无法改变变量值,也不能删除 (执行 unset) 变量。变量会一直存在到 Shell 进程关闭。等同于 readonly 命令。
  • -u 声明变量为大写字母。
  • -x 该变量输出为环境变量。等同于 export 命令。

4. 预定义环境变量

4.1. PS1

命令提示符通常是 $,对于 root 用户来说是 #。这个符号是环境变量 PS1 决定的。

用户可以自行修改这个环境变量的值来自定义命令提示符。

命令提示符的定义可以包含特殊的转义字符:

  • \a: 响铃,计算机发声一次。
  • \d: 以星期、月、日格式表示当前日期,例如 Mon May 26
  • \h: 本机的主机名。
  • \H: 完整的主机名。
  • \j: 运行在当前 Shell 会话的工作数。
  • \l: 当前终端设备名。
  • \n: 一个换行符。
  • \r: 一个回车符。
  • \s: Shell 的名称。
  • \t: 24 小时制的 hours:minutes:seconds 格式的当前时间。
  • \T: 12 小时制的当前时间。
  • \@: 12 小时制的 AM/PM 格式的当前时间。
  • \A: 24 小时制的 hours:minutes 格式的当前时间。
  • \u: 当前用户名。
  • \v: Shell 的版本号。
  • \V: Shell 的版本号和发布号。
  • \w: 当前的工作路径。
  • \W: 当前目录名。
  • \!: 当前命令在命令历史中的编号。
  • \#: 当前 shell 会话中的命令数。
  • \$: 普通用户显示为 $ 字符,根用户显示为 # 字符。
  • \[: 非打印字符序列的开始标志。
  • \]: 非打印字符序列的结束标志。

比如 $PS1 值为 \u@\h:\w\$ ,显示效果为 username@hostname:~$

也可以为 PS1 加入文字颜色定义 (\033 可用 \e 代替):

  • \033[0;30m 黑色
  • \033[1;30m 深灰色
  • \033[0;31m 红色
  • \033[1;31m 浅红色
  • \033[0;32m 绿色
  • \033[1;32m 浅绿色
  • \033[0;33m 棕色
  • \033[1;33m 黄色
  • \033[0;34m 蓝色
  • \033[1;34m 浅蓝色
  • \033[0;35m 粉红
  • \033[1;35m 浅粉色
  • \033[0;36m 青色
  • \033[1;36m 浅青色
  • \033[0;37m 浅灰色
  • \033[1;37m 白色

和文字背景颜色定义 (\033 可用 \e 代替):

  • \033[0;40m 蓝色
  • \033[1;44m 黑色
  • \033[0;41m 红色
  • \033[1;45m 粉红
  • \033[0;42m 绿色
  • \033[1;46m 青色
  • \033[0;43m 棕色
  • \033[1;47m 浅灰色

注意: 必须把把颜色信息放在 \[\] 中间,否则长于一行的命令将不会自动换行。

比如 PS1='\[\033[0;31m\]<\u@\h \W>\$' 则显示红色的命令提示符,但这也会导致后面命令变成红色。可以在结尾添加另一个特殊代码 \[\033[00m\] ,表示将其后的文本恢复到默认颜色。所以应该写成 PS1='\[\033[0;31m\]<\u@\h \W>\$\[\033[00m\]'

比如 PS1='\[\033[0;41m\]<\u@\h \W>\$\[\033[0m\] ' 这是一个带红色背景的命令提示符。

4.2. PS2 PS3 PS4

环境变量 PS2 是输入多行命令时的行首提示符,默认为 >

$ echo "hello
> world"

环境变量 PS3 是使用 select 命令时,系统输入菜单的提示符。

环境变量 PS4 默认值为 + 。执行 bash -x {script_name}.sh 时,脚本的每一行命令被执行前都会被输出到终端,并且每行行首出现 PS4 定义的提示符。

$ cat>test.sh<<'EOF'
#!/bin/bash
echo "hello world1"
echo "hello world2"
echo "hello world3"
EOF

$ bash -x test.sh
+ echo 'hello world1'
hello world1
+ echo 'hello world2'
hello world2
+ echo 'hello world3'
hello world3


Last Update: 2023-08-30 Wed 14:44

Generated by: Emacs 28.2 (Org mode 9.5.5)   Contact: [email protected]

若正文中无特殊说明,本站内容遵循: 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议