Shell 变量全解

变量介绍

shell变量是一种很“弱”的变量,默认情况下,一个变量保存一个串,shell不关心这个串是什么含义。所以若要进行数学运算,必须使用一些命令例如let、declare、expr、双括号等。

shell变量可分为两类:局部变量和环境变量。局部变量只在创建它们的shell中可用。而环境变量则可以在创建它们的shell及其派生出来的任意子进程中使用。有些变量是用户创建的,其他的则是专用shell变量。

变量名必须以字母或下划线字符开头。其余的字符可以是字母、数字(0~9)或下划线字符。任何其他的字符都标志着变量名的终止。名字是大小写敏感的。

给变量赋值时,等号周围不能有任何空白符。为了给变量赋空值,可以在等号后跟一个换行符。

用set命令可以查看所有的变量,unset var命令可以清除变量var,var相当于没有定义过。readonly var可以把var变为只读变量,定义之后不能对var进行任何更改。

shell变量常见引用方式如下:

引用格式 含义介绍
${var} 变量值。也可以写成$var,但是不推荐。
${#var} 变量长度。例如var="HelloWorld",则${#var}返回值为10。 注意,引号””是界定符号,而不是变量中的字符。
${var:start_index} 返回从start_index开始一直到字符串结尾。
start_index为0表示从第一个字符开始,
start_index为0-x表示从倒数第x个字符开始。
例如var="0123456789"。则${var:0}返回"0123456789",${var:6}返回"6789",${var:0-3}返回”789”。
${var:start_index:length} 返回从start_index开始的length个字符,length可以为负数。var="0123456789",${var:2:5}返回"23456",${var:5:-2}返回"567"(-2表示剩余2个字符不要),${var:0-3:-1}返回"78"。
${var#string} 返回从左边删除string后的字符串,尽量短的去匹配。例如var="https://127.0.0.1/index.php", ${var#*/}返回"/127.0.0.1/index.php"
${var##string} 返回从左边删除string后的字符串,尽量长的去匹配。例如var="https://127.0.0.1/index.php",${var##*/}返回"index.php"
${var%string} 返回从右边删除string后的字符串,尽量短的去匹配。例如var="https://127.0.0.1/index.php",${var%/*}返回"https://127.0.0.1"
${var%string} 返回从右边删除string后的字符串,尽量长的去匹配。例如var="https://127.0.0.1/index.php",${var%/*}返回"https:"
${var:-newstring} 如果var为空,或者未定义,则返回newstring;否则返回原值。
${var:=newstring} 如果var为空,或者未定义,则返回newstring,并把newstring赋值给var;否则返回原值。
${var:+newstring} 如果var不为空,则返回newstring;如果var为空,则返回空值。
${var:?newstring} 如果var为空或者未定义,则将newstring写入标准错误流,本语句失败;如果var不为空,则返回原值。
${var/substring/newstring} 返回var中第一个substring被替换成newstring的字符串。例如var="2345432", ${var/3/Hello}返回"2Hello45432"
${var//substring/newstring} 返回var中所有substring被替换成newstring的字符串。例如var="2345432", ${var//3/Hello}返回"2Hello454Hello2"
$(command) 返回command命令执行后的所输出的结果。例如$(date),返回date命令执行后的输出,同date命令
$((算数表达式)) 返回双括号中算数运算的结果。例如$((20+5*6)),返回50

环境变量

环境变量的定义方法如下:

1
export var=value

shell在初始化的时候会在执行profile等初始化脚本,脚本中定义了一些环境变量,这些变量会在创建子进程时传递给子进程。
用env命令可以查看当前的环境变量。常用的系统环境变量如下:

环境变量 含义
_ 上一条命令的最后一个参数 $_
BASH 展开为调用bash实例时使用的全路径名
CDPATH cd命令的搜索路径。它是以冒号分隔的目录列表,shell通过它来搜索cd命令指定的目标目录。例如.:~:/usr
EDITOR 内置编辑器emacs、gmacs或vi的路径名
ENV 每一个新的bash shell(包括脚本)启动时执行的环境文件。通常赋予这个变量的文件名是.bashrc。
EUID 展开为在shell启动时被初始化的当前用户的有效ID
GROUPS 当前用户所属的组
HISTFILE 指定保存命令行历史的文件。默认值是~/.bash_history。如果被复位,交互式shell退出时将不保存命令行历史
HISTSIZE 记录在命令行历史文件中的命令数。默认是500
HOME 主目录。未指定目录时,cd命令将转向该目录
IFS 内部字段分隔符,一般是空格符、制表符和换行符,用于由命令替换,循环结构中的表和读取的输入产生的词的字段划分
LANG 用来为没有以LC_开头的变量明确选取的种类确定locale类
OLDPWD 前一个工作目录
PATH 命令搜索路径。一个由冒号分隔的目录列表,shell用它来搜索命令,一个普通值为 /usr/gnu/bin:/usr/local/bin:/usr/ucb:/usr/bin
PPID 父进程的进程ID
PS1 主提示符串,默认值是$
PS2 次提示符串,默认值是>
PS3 与select命令一起使用的选择提示符串,默认值是#?
PS4 当开启追踪时使用的调试提示符串,默认值是+。追踪可以用set –x开启
PWD 当前工作目录。由cd设置
RANDOM 每次引用该变量,就产生一个随机整数。随机数序列可以通过给RANDOM赋值来初始化。如果RANDOM被复位,即使随后再设置,它也将失去特定的属性
REPLY 当没有给read提供参数时设置
SHELL 当调用shell时,它扫描环境变量以寻找该名字。shell给PATH、PS1、PS2、MAILCHECK和IFS设置默认值。HOME和MAIL由login(1)设置
SHELLOPTS 包含一列开启的shell选项,比如braceexpand、hashall、monitor等
UID 展开为当前用户的用户ID,在shell启动时初始化

可以为某个命令单独设置环境变量,而不会影响后续的命令执行,比如:

1
LD_LIBRARY_PATH=./lib:$LD_LIBRARY_PATH ./make_ext4fs

数值变量

shell中默认把变量值当作字符串,例如:

1
2
3
age=22
age=${age}+1
echo ${age}

输出结果为22+1,而不是23,因为shell将其解释为字符串,而不是数学运算。

可以用let命令使其进行数学运算,例如:

1
let age=${age}+1

也可以用typeset或者declare把变量定义为整型。例如:

1
2
3
typeset -i age=22     
age=${age}+1
echo ${age}

typeset的 -i 选项把age定义为整型的了。此后每次运算,都把age的右值识别为算术表达式或数字。最终的输出结果是23

数组

在shell中可以使用数组,例如:

1
2
3
array[0]=0
array[1]=1
array[2]=2

则array就是一个数组,也可以这样给数组初始化:

1
array=(0 1 2) // 元素之间以空格分隔

数组变量操作

操作 介绍
${array[$*]} 访问array中某个元素
${#array[*]} 返回数组元素个数
${array[*]}
${array[@]}
的返回值即数组的所有元素组成的串
${array[*]:0:2} 返回第一个和第二个元素组成的串。0表示开始的位置,2表示要返回的元素个数,开始位置可以为0-2(0减去2)之类的,表示从倒数第二个元素开始。
$array[${#array[*]}]=value 追加数组元素
unset array[$i] 删除某个数组元素
unset array 删除整个数据

下面写个稍微复杂点的例子:

1
2
3
4
5
6
7
8
9
for ((i=0; i<100; i++))
do
array[$i]=$i
done

for ((i=0; i<100; i++))
do
echo ${array[$i]}
done

字符串转化为数组

1
2
str="one two three four"
arr=($str)

如果字符串分隔符不是空格,则采用下面方法分割成数组:

1
2
3
4
5
6
7
8
9
10
11
#!/bin/sh
str="one,two,three,four"
OLD_IFS="$IFS"
IFS=","
arr=($str)
IFS="$OLD_IFS"

for var in ${arr[@]}
do
echo "$var"
done

OLD_IFS用于保存原分隔符,用于后续恢复。

特殊变量

变量 含义
$0 当前脚本的文件名
$n n是正整数。$0当前脚本名称,$1是第一个参数,$2是第二个参数,等等
$# 传入脚本的参数的个数
$* 所有的位置参数(作为单个字符串)
$@ 所有的位置参数(每个都作为独立的字符串)。
$? 当前shell进程中,上一个命令的返回值,如果上一个命令成功执行则$?的值为0,否则为其他非零值,常用做if语句条件
$$ 当前shell进程的pid
$! 后台运行的最后一个进程的pid
$- 显示shell使用的当前选项
$_ 之前命令的最后一个参数

$*$@都表示传给脚本或者函数的参数列表,当使用双引号包裹时,"$*"是一个字符串,而"$@"则像一个数组:

1
2
3
4
5
6
7
for var in "$*";do
echo $var
done
echo "-------------"
for var in "$@";do
echo $var
done

上述代码输入结果为:

1
2
3
4
5
6
$ ./test.sh xxx yyy zzz
xxx yyy zzz
-------------
xxx
yyy
zzz

指定变量类型:typeset 与 declare

declare和typeset是bash的内建命令,它们是完全相同的,可以用来限定变量的属性,如整型、大小写、宽度、左右对齐等。这是在某些编程语言中使用的定义类型不严格的方式。
当用typeset或declare改变一个变量的属性时,这种改变是永久的。
命令declare是bash版本2之后才有的,命令typeset也可以在ksh脚本中运行。

常用命令参数:

  • -r 设置变量为只读
  • -i 设置变量为整数
  • -a 设置变量为数组array
  • -f 如果后面没有参数的话会列出之前脚本定义的所有函数,如果有参数的话列出以参数命名的函数
  • -x 设置变量在脚本外也可以访问到
  • -u 将一个变量的字符变成大写
  • -l 将一个变量的字符变成小写

变量应用举例

遍历字符串

1
2
3
4
5
6
7
local var=$1
local len=${&#35;var}
local i=0
while [ $i -lt $len ];do
echo ${var:$i:1}
typeset i=$((i+1))
done

参考文档