Shell变量

Shell变量

Bash中的参数扩展

参数是一个存储数值的实体,并由名称,数字或特定符号所引用。

  • 被名称引用的参数称为变量。
  • 被数字引用的参数称为位置参数。
  • 被特定符号引用的参数具有特殊的含义和用途,被称为Bash的特殊内部变量引用。

参数扩展是从引用的实体取值的过程,就像扩展变量打印它的值。
字符’$’会引导参数扩展。将要扩展的参数名或者符号可以放到大括号中。大括号是可选的,但却可以保护待扩展的变量,使得紧跟大括号后的内容不会被扩展

基本参数扩展

基本参数扩展形式如下

1
2
$parameter
${parameter}

如果参数名后还有其他字符,这时候大括号是必须的。示例如下

1
2
3
4
5
name='tqd'
echo $names
#输出内容为空
echo ${name}s
#输出内容为tqds

上述示例 第一个echo命令的输出为空,是因为参数名names是未定义的。对于未使用大括号的参数扩展,Bash会从字符’$’开始到最后一个有效字符结束的所有可用的字符序列解释为参数名。当使用大括号时,会强制Bash只解释大括号内的名称。
备注:对于访问$9之后的位置参数也同样需要使用大括号,如下所示

1
2
3
4
5
set 1a 2a 3a 4a 5a 6a 7a 8a 9a 10a 11a
echo $10
#输出为1a0
echo ${10}
#输出为10a

注意:参数名是大小写敏感的。

间接参数扩展

间接参数扩展的基本形式如下

1
${!parameter}

上述语句,被引用的参数不是parameter自身,而是parameter的值。示例如下

1
2
3
4
5
6
temp='tqd'
userName='temp'
echo ${!userName}
#输出为tqd
echo ${userName}
#输出为temp

大小写扩展

基本形式如下

1
2
3
4
5
6
${parameter^}    #将参数值中的第一个字符改为大小(对字母才有效)
${parameter^^} #将参数值中的所有字符改为大写
${parameter,} #将参数值中的第一个字符改为小写
${parameter,,} #将参数值中的所有字符改为小写
${parameter~} #将参数值中的第一个字符的大小写取反
${parameter~~} #将参数值中的所有字符的大小写取反

示例如下

1
2
3
4
5
6
7
8
9
10
11
12
13
person_alias="QidongTan"
echo ${person_alias^}
#输出为QidongTan
echo ${person_alias^^}
#输出为QIDONGTAN
echo ${person_alias,}
#输出为qidongTan
echo ${person_alias,,}
#输出为qidongtan
echo ${person_alias~}
#输出为qidongTan
echo ${person_alias~~}
#输出为qIDONGtAN

变量名扩展

基本形式如下

1
2
${!PREFIX@}
${!PREFIX*} 列出所有以PREFIX开头的变量名,并以空格隔开

示例如下

1
2
3
4
5
6
echo ${!BASH@}
输出为:BASH BASHOPTS BASHPID BASH_ALIASES BASH_ARGC BASH_ARGV BASH_ARGV0 BASH_CMDS BASH_COMMAND BASH_COMPLETION_VERSINFO BASH_LINENO BASH_REMATCH BASH_SOURCE BASH_SUBSHELL BASH_VERSINFO BASH_VERSION
for parameter in ${!BASH*}
do
echo "The value of ${parameter} is ${!parameter}"
done

字符串移除

基本形式

1
2
3
4
${parameter#PATTERN}    #从参数值的开始,移除最短匹配PATTERN的子串
${parameter##PATTERN} #从参数值的开始,移除最长匹配PATTERN的子串
${parameter%PATTERN} #从参数值的结尾向前,移除最短匹配PATTERN的子串
${parameter%%PATTERN} #从参数值的结尾向前,移除最长匹配PATTERN的子串

示例如下

1
2
3
4
5
6
7
8
9
str=test.tar.gz
echo ${str#*.}
#输出为tar.gz
echo ${str##*.}
#输出为gz
echo ${str%.*}
#输出为test.tar
echo ${str%%.*}
#输出为test

字符串搜索与替换

基本形式

1
2
3
4
${parameter/PATTERN/STRING}    # 将参数值中第一个匹配PATTERN的子串替换为STRING
${parameter//PATTERN/STRING} # 将参数值中所有匹配PATTERN的子串替换为STRING
${parameter/PATTERN} # 将参数值中第一个匹配PATTERN的子串删除
${parameter//PATTERN} # 将参数值中所有匹配PATTERN的子串删除

示例如下

1
2
3
4
5
6
7
8
9
str="You are a beautiful girl,I do love You"
echo ${str/You/She}
#输出为She are a beautiful girl,I do love You
echo ${str//You/She}
#输出为She are a beautiful girl,I do love She
echo ${str/You}
#输出为are a beautiful girl,I do love You
echo ${str//You}
#输出为are a beautiful girl,I do love

字符串长度

基本形式如下

1
${#parameter} #获取参数值的长度

示例如下

1
2
3
str="You,a beautiful girl!"
echo ${#str}
#输出为21

子字符串扩展

基本形式如下

1
2
${str:OFFSET}        #从OFFSET开始,到参数值的结尾的子字符串
${str:OFFSET:LENGTH} #从OFFSET开始,获取长度为LENGTH的子字符串

示例如下

1
2
3
4
5
6
7
8
9
str="12345678"
echo ${str:2:2}
#输出为34
echo ${str:7}
#输出为8
echo ${str:9}
#输出为空
echo ${str:7:10}
#输出为8

使用默认值

基本形式如下

1
2
${parameter:-defaultValue} #当parameter未定义或者为空时,扩展为defaultValue
${parameter-defaultValue} #当parameter未定义时,扩展为defaultValue

示例如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
t=''
echo "empty t:- ${t:-defaultValue}"
#输出为empty t:- defaultValue
echo "empty t- ${t-defaultValue}"
#输出为empty t-
echo "empty t ${t}"
#输出为empty t
unset t
echo "undefined t:- ${t:-defaultValue}"
#输出为undifined t:- defaultValue
echo "undefined t- ${t-defaultValue}"
#输出为undifined t- defaultValue
echo "undefined t ${t}"
#输出为undefined t

指定默认值

基本形式如下

1
2
${parameter:=defaultValue} #当parameter未定义或者为空时,将parameter的值赋值为defaultValue并且扩展为defaultValue
${parameter=defaultValue} #当parameter未定义时,将parameter的值赋值为defaultValue并且扩展为defaultValue

示例如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#!/bin/bash
t=''
echo "empty t:= ${t:=defaultValue}"
#输出为empty t:= defaultValue
echo "empty t ${t}"
#输出为empty t defaultValue
t=''
echo "empty t= ${t=defaultValue}"
#输出为empty t=
echo "empty t ${t}"
#输出为empty t
unset t
echo "undefined t:= ${t:=defaultValue}"
#输出为undefined t:= defaultValue
echo "undefined t ${t}"
#输出为undefined t defaultValue
unset t
echo "undefined t= ${t=defaultValue}"
#输出为undefined t= defaultValue
echo "undefined t ${t}"
#输出为undefined t defaultValue

使用替代值

基本形式如下

1
2
${parameter:+replaceValue} # 当parameter不为空时,扩展为替代值
${parameter+replaceValue} # 当parameter为空或者不为空时,扩展为替代值

示例如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
t="1"
echo "no empty t:+${t:+replaceValue}"
#输出为no empty t:+replaceValue
echo "no empty t${t}"
#输出为no empty t1
t="1"
echo "no empty t+${t+replaceValue}"
#输出为no empty t+replaceValue
echo "no empty t${t}"
#输出为no empty t1
t=""
echo "empty t:+${t:+replaceValue}"
#输出为empty t:+
echo "empty t${t}"
#输出为empty t
t=""
echo "empty t+${t+replaceValue}"
#输出为empty t+replaceValue
echo "empty t${t}"
#输出为empty t
unset t
echo "undefined t:+${t:+replaceValue}"
#输出为undefined t:+
echo "undefined t${t}"
#输出为undefined t
unset t
echo "undefined t+${t+replaceValue}"
#输出为undefined t+
echo "undefined t${t}"
#输出为undefined t

缺失或者为空错误提示

基本形式如下

1
2
${parameter:?Error:parameter is undefined or empty} #当parameter未定义或者为空时,报错
${parameter:Error:parameter is undefined} #当parameter未定义时,报错

示例如下

1
2
3
4
5
6
7
8
9
10
11
12
13
t=""
echo ${t:?Error t is empty or undefined}
#报错 ./use-error.sh: line 3: t: Error t is empty or undefined
t=""
echo ${t?Error t is undefined}
#不报错
unset t
echo ${t:?Error t is empty or undefined}
#报错 ./use-error.sh: line 3: t: Error t is empty or undefined
unset t
echo ${t?Error t is undefined}
./use-error.sh: line 8: t: Error t is undefined
#报错

Bash的内部变量

常用内部变量

变量名 备注
$BASH Bash实例的全路径名 /bin/bash
$HOME 当前用户的home目录 一般是/home/${USER}
$IFS 此变量决定当Bash解析字符串时,如何失败字段或者单词分割线 默认为空格,制表符和换行
$OSTYPE 操作系统的类型
$SECONDS 当前脚本已执行的秒数
$TMOUT 作为Bash内部命令read的默认超时值,指定秒数内未输入,跳过输入,值为空
$UID 当前用户的帐号标识码,与/etc/passwd记录相同

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
echo $BASH
echo $HOME
set 1 2 3
IFS=',:'
#这里再一次体现"$@""@*"的区别"$@"="$1 $2 $3 $4..." "$*"="${1}${IFS}${2}${IFS}..."
echo "$@"
echo "$*"
echo $*
echo $TMOUT
TMOUT=3
read a;
echo "The value of a is:${a}"

echo $UID

Bash中的位置参数和特殊参数

位置参数

Bash的位置参数是除了0以外的一个或者多个数字表示的参数。

位置参数是当Shell或者Shell的函数被引用时由Shell或者Shell函数的参数赋值,并且可以使用Bash内部命令set来重新赋值,位置参数可以被引用为${N},当N只有一个数字时,可写成$N

注意:多于一个数字的位置参数在扩展时必须放到大括号中,比如位置参数10,写成${10}.

位置参数不能通过赋值语句来赋值,只能通过Bash内部命令set和shift来设置和取消它们,当shell运行时,位置参数会被临时替换。

  • set用法为 set 1 2 3..N;设置1,2,3…N位置的参数。
  • shift用法为: shift N;删除前N个参数。ff

示例如下:

1
2
3
4
5
6
7
8
set 1a 2b 3c 4d 5e 6f 7g 8h 9i 10j
echo "$@"
#输出为1a 2b 3c 4d 5e 6f 7g 8h 9i 10j
echo ${10}
#输出为10j
shift 2
echo "$@"
#输出为3c 4d 5e 6f 7g 8h 9i 10j

特殊参数

符号 描述
* 扩展为从1开始的所有位置参数,如果在双引号之间,即”$*”,则扩展为包含每一个参数值的单词,每个参数值之间用特殊变量IFS的第一个字符隔开,即”$*“=”$1c$2c$3”
@ 扩展为从1开始的所有位置参数,如果在双引号之间,即”$@”等价于”$1” “$2” …
# 扩展为位置参数的个数,十进制表示
? 最近一个前台命令的推出状态,通常状态0表示已经没有任何错误地结束运行
- 扩展为当前的选项标志,这些选项是在调用时,或者由内部命令set指定,或者shell自身指定
$ 扩展为当前脚本的进程号
! 扩展为上一个后台命令的进程号
0 扩展为Shell或者Shell脚本的名称,它是Shell初始化时设置的。如果Bash调用时带有脚本文件作为参数,那么$0就设置为脚本的文件名
_ 在Shell启动时,它被设为开始运行Shell脚本或者脚本的路径,随后,扩展为上一个命令的最后一个参数

示例@,*,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#!/bin/bash
set 1a 2b 3c 4d 5f '6d d'
oldIfs=$IFS
IFS=","
echo $*
echo "$*"
echo $@
echo "$@"
echo "arg count:$#"
echo 'for arg in $*'
i=1;
for arg in $*
do
echo "arg ${i}: ${arg}"
let i++
done
i=1
echo 'for arg in "$*"'
for arg in "$*"
do
echo "arg ${i}: ${arg}"
let i++
done
echo 'for arg in $@'
i=1
for arg in $@
do
echo "arg ${i}: ${arg}"
let i++
done
echo 'for arg in "$@"'
i=1
for arg in "$@"
do
echo "arg ${i}: ${arg}"
let i++
done
IFS="$oldIfs"

输出为

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
1a 2b 3c 4d 5f 6d d
1a,2b,3c,4d,5f,6d d
1a 2b 3c 4d 5f 6d d
1a 2b 3c 4d 5f 6d d
arg count:6
for arg in $*
arg 1: 1a
arg 2: 2b
arg 3: 3c
arg 4: 4d
arg 5: 5f
arg 6: 6d d
for arg in "$*"
arg 1: 1a,2b,3c,4d,5f,6d d
for arg in $@
arg 1: 1a
arg 2: 2b
arg 3: 3c
arg 4: 4d
arg 5: 5f
arg 6: 6d d
for arg in "$@"
arg 1: 1a
arg 2: 2b
arg 3: 3c
arg 4: 4d
arg 5: 5f
arg 6: 6d d

示例0

1
2
#!/bin/bash
echo $0

输出

1
2
3
4
tqd@tqd-pc:/work/study/shell-learn/variable$ . arg0.sh
/bin/bash
tqd@tqd-pc:/work/study/shell-learn/variable$ ./arg0.sh
./arg0.sh

示例_

1
2
3
4
#!/bin/bash
echo $_
echo "underscore msg"
echo $_

输出为

1
2
3
4
tqd@tqd-pc:/work/study/shell-learn/variable$ ./underscore.sh
./underscore.sh
underscore msg
underscore msg

使用declare指定变量的类型

declare是Bash的内部命令,它与Bash的另一个内部命令typeset的用法与用途完全相同。

  1. 直接使用declare将打印所有变量的值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
tqd@tqd-pc:/data/me/blog-test$ declare | more
BASH=/bin/bash
BASHOPTS=checkwinsize:cmdhist:complete_fullquote:expand_aliases:e xtglob:extquote:force_fignore:globasciiranges:histappend:interact ive_comments:progcomp:promptvars:sourcepath
BASH_ALIASES=()
BASH_ARGC=([0]="0")
BASH_ARGV=()
BASH_CMDS=()
BASH_COMPLETION_VERSINFO=([0]="2" [1]="10")
BASH_LINENO=()
BASH_SOURCE=()
BASH_VERSINFO=([0]="5" [1]="0" [2]="17" [3]="1" [4]="release" [5] ="x86_64-pc-linux-gnu")
BASH_VERSION='5.0.17(1)-release'
CHROME_DESKTOP=code-url-handler.desktop
CINNAMON_VERSION=4.6.7
--More--
  1. -r 将指定变量定义为只读变量
1
2
3
4
5
#!/bin/bash
declare -r a=1;
echo "The readonly variable is:${a}"
a=2
unset a

输出

1
2
3
4
tqd@tqd-pc:/work/study/shell-learn/variable$ ./declare-test.sh
The readonly variable is:1
./declare-test.sh: line 4: a: readonly variable
./declare-test.sh: line 5: unset: a: cannot unset: readonly variable
  1. -i将变量定义为整型变量,赋予整型变量的任何类型的值都将转化为整数
1
2
3
4
5
6
7
8
9
10
11
declare -i n;
n=1
echo "n=1 is $n"
n=2
echo "n=2 is $n"
n='a';
echo "n=a is $n"
n='b';
echo "n=b is $n"
n='abc';
echo "n=abc is $n"

输出为

1
2
3
4
5
n=1 is 1
n=2 is 2
n=a is 1
n=b is 0
n=abc is 0
  1. -x 将指定变量通过环境变量输出到后续命令
  2. -p 显示指定变量的属性和值
1
2
declare -i m=1;
declare -p m

输出为

1
declare -i m="1"

Bash中的数组变量

一个数组时包含多个值的变量,任何变量都可以作为一个数组使用,数组的大小没有限制,成员变量也不需要连续分配。数组的索引是从0开始。声明一个数组变量的语法如下

1
2
3
#index必须是正数,或是一个值为正数的算术表达式
arrayName[index]=value
declare -a arrayName=('1' '2' '3')

数组变量可以使用复合赋值格式

1
arrayName=(value1,value2,value3...valueN)

引用数组中的某一项的值,必须时哟哦那个花括号,如果索引编号是‘@’或者’*’时,这些属性都会被应用到数组变量中。如果索引时不指定索引编号,则引用的数组第一项的值,即使用索引编号0.使用unset可以取消一个数组或数组的成员变量,如下所示

1
2
3
4
#取消数组变量array1的定义
unset array1
#取消数组成员变量2的定义
unset array1[2]

备注:Bash的各种参数扩展也可以用于数组变量
示例:

1
2
3
4
5
6
7
8
9
10
#!/bin/bash
a=(1 2 3)
echo "${a[1]}"
echo "a is ${a}"
unset a[1]
echo "${a[1]}"
for item in ${a[*]}
do
echo ${item}
done

输出

1
2
3
4
5
6
7
8
9
10
#!/bin/bash
a=(1 2 3)
echo "${a[1]}"
echo "a is ${a}"
unset a[1]
echo "${a[1]}"
for item in ${a[*]}
do
echo ${item}
done