文章目录
- 1、变量和参数的介绍
- (1)变量替换
- $(...)
- (2)特殊的变量类型
- export
- 位置参数
- shift
- 2、引用
- (1)引用变量
- (2)转义
- 3、条件判断
- (1)条件测试结构
- (2)文件测试操作符
- (3)其他比较操作符
- 整数比较
- 字符串比较
- compound comparison
- 4、数字常量
- 5、变量重游
- (1) 内部变量
- 内建变量
- 位置参数
- 其他的特殊参数
- (2) 操作字符串
- 字符串长度
- 匹配字符串开头的子串长度
- 索引
- 提取子串
- 子串削除
- 子串替换
- (3) 参数替换
- 处理和(或)扩展变量
- (4)指定变量的类型: 使用 declare 或者 typeset
- (5)变量的间接引用
- (6)$RANDOM: 产生随机整数
- (7)双圆括号结构
1、变量和参数的介绍
(1)变量替换
$(…)
使用 $(…) 机制来进行变量赋值(这是一种比后置引用(反引号`)更新的一种方法)。 事实上这两种方法都是命令替换的一种形式。
(2)特殊的变量类型
export
一个脚本只能够 export 变量到这个脚本所产生的子进程, 也就是说只能够对这个脚本所产生的命令和进程起作用。 如果脚本是从命令行中调用的, 那么这个脚本所 export 的变量是不能影响命令行环境的。 也就是说, 子进程是不能够 export 变量来影响产生自己的父进程的环境的。
位置参数
从命令行传递到脚本的参数: $0, $1, $2, $3 . . .
$0 就是脚本文件自身的名字, $1 是第一个参数, $2 是第二个参数, $3 是第三个参数, 然后是第四个。 $9 之后的位置参数就必须用大括号括起来了, 比如, ${10}, ${11}, ${12} 。
两个比较特殊的变量 $* 和 $@ 表示所有的位置参数。
shift
shift 命令会重新分配位置参数, 其实就是把所有的位置参数都向左移动一个位置。
$1 <— $2, $2 <— $3, $3 <— $4, 等等。
原来的 $1 就消失了, 但是 $0 (脚本名) 是不会改变的。 如果传递了大量的位置参数到脚本中, 那么 shift 命令允许你访问的位置参数的数量超过 10 个, 当然 {} 标记法也提供了这样的功能。
2、引用
(1)引用变量
双引号中通过直接使用变量名的方法来引用变量。单引号 (’ ') 操作与双引号基本一样, 但是不允许引用变量, 因为 $ 的特殊意义被关闭了。
(2)转义
- \0xx
转换为八进制的 ASCII 码, 等价于 0xx
3、条件判断
(1)条件测试结构
- test, /usr/bin/test, [ ], 和 /usr/bin/[ 都是等价命令
- [[ ]] 结构比 [ ] 结构更加通用。 这是一个扩展的 test 命令, 是从 ksh88 中引进的。
- (( )) 结构扩展并计算一个算术表达式的值
(2)文件测试操作符
- -e
文件存在 - -a
文件存在。这个选项的效果与 -e 相同。 但是它已经被 “弃用” 了, 并且不鼓励使用。 - -f
表示这个文件是一个一般文件 (并不是目录或者设备文件) - -s
文件大小不为零 - -d
表示这是一个目录 - -b
表示这是一个块设备 (软盘, 光驱, 等等。) - -c
表示这是一个字符设备 (键盘, modem, 声卡, 等等。) - -p
这个文件是一个管道 - -h
这是一个符号链接 - -L
这是一个符号链接 - -S
表示这是一个 socket - -t
文件(描述符)被关联到一个终端设备上。这个测试选项一般被用来检测脚本中的 stdin([ -t 0 ]) 或者 stdout([ -t 1 ]) 是否来自于一个终端。 - -r
文件是否具有可读权限(指的是正在运行这个测试命令的用户是否具有读权限) - -w
文件是否具有可写权限(指的是正在运行这个测试命令的用户是否具有写权限) - -x
文件是否具有可执行权限(指的是正在运行这个测试命令的用户是否具有可执行权限) - -g
set-group-id(sgid) 标记被设置到文件或目录上。
如果目录具有 sgid 标记的话, 那么在这个目录下所创建的文件将属于拥有这个目录的用户组, 而不必是创建这个文件的用户组。 这个特性对于在一个工作组中共享目录非常有用。 - -u
set-user-id (suid) 标记被设置到文件上。
如果一个 root 用户所拥有的二进制可执行文件设置了 set-user-id 标记位的话, 那么普通用户也会以 root 权限来运行这个文件。 - -k
设置粘贴位
对于 “粘贴位” 的一般了解, save-text-mode 标志是一个文件权限的特殊类型。 如果文件设置了这个标志, 那么这个文件将会被保存到缓存中, 这样可以提高访问速度。 - -O
判断你是否是文件的拥有者 - -G
文件的 group-id 是否与你的相同 - -N
从文件上一次被读取到现在为止, 文件是否被修改过 - f1 -nt f2
文件 f1 比文件 f2 新 - f1 -ot f2
文件 f1 比文件 f2 旧 - f1 -ef f2
文件 f1 和文件 f2 是相同文件的硬链接 - !
“非” —— 反转上边所有测试的结果(如果没给出条件, 那么返回真)。
(3)其他比较操作符
二元比较操作符用来比较两个变量或数字。 注意整数比较与字符串比较的区别。
整数比较
- -eq
等于
if [ “$a” -eq “$b” ] - -ne
不等于
if [ “$a” -ne “$b” ] - -gt
大于
if [ “$a” -gt “$b” ] - -ge
大于等于
if [ “$a” -ge “$b” ] - -lt
小于
if [ “$a” -lt “$b” ] - -le
小于等于
if [ “$a” -le “$b” ] - <
小于(在双括号中使用)
((“$a” < “$b”)) - <=
小于等于(在双括号中使用)
((“b”)) - >
大于(在双括号中使用)
((“$a” > “$b”)) - >=
大于等于(在双括号中使用)
((“$a” >= “$b”))
字符串比较
- =
等于
if [ “$a” = “$b” ] - ==
等于
if [ “$a” == “$b” ]
与 = 等价。 - !=
不等号
if [ “$a” != “$b” ]
这个操作符将在 [[ … ]] 结构中使用模式匹配。 - <
小于, 按照 ASCII 字符进行排序
if [[ “$a” < “$b” ]]
if [ “$a” \< “$b” ]
注意 “<” 使用在 [ ] 结构中的时候需要被转义。 - >
大于, 按照 ASCII 字符进行排序
if [[ “$a” > “$b” ]]
if [ “$a” \> “$b” ]
注意 “>” 使用在 [ ] 结构中的时候需要被转义。 - -z
字符串为 “null” , 意思就是字符串长度为零 - -n
字符串不为 “null” 。
compound comparison
- -a
逻辑与
exp1 -a exp2 如果表达式 exp1 和 exp2 都为真的话, 那么结果为真。 - -o
逻辑或
exp1 -o exp2 如果表达式 exp1 和 exp2 中至少有一个为真的话, 那么结果为真。
4、数字常量
shell 脚本在默认情况下都是把数字作为 10 进制数来处理, 除非这个数字采用了特殊的标记或者前缀。
如果数字以 0 开头的话那么就是 8 进制数。 如果数字以 0x 开头的话那么就是 16 进制数。 如果数字中间嵌入了 # 的话, 那么就被认为是 BASE#NUMBER 形式的标记法(有范围和符号限制)。
5、变量重游
(1) 内部变量
内建变量
这些变量将会影响 bash 脚本的行为。
- $BASH
Bash 的二进制程序文件的路径
- $BASH_ENV
这个环境变量会指向一个 Bash 的启动文件, 当一个脚本被调用的时候, 这个启动文件将会被读取。 - $BASH_SUBSHELL
这个变量用来提示子 shell 的层次。 这是一个 Bash 的新特性, 直到版本 3 的 Bash 才被引入近来。 - $BASH_VERSINFO[n]
这是一个含有 6 个元素的数组, 它包含了所安装的 Bash 的版本信息。 这与下边的 $BASH_VERSION 很相像, 但是这个更加详细一些。
- $BASH_VERSION
安装在系统上的 Bash 版本号
- $DIRSTACK
在目录栈中最顶端的值。 (将会受到 pushd 和 popd 的影响)
这个内建变量与 dirs 命令相符, 但是 dirs 命令会显示目录栈的整个内容。 - $EDITOR
脚本所调用的默认编辑器, 通常情况下是 vi 或者是 emacs 。 - $EUID
“有效” 用户 ID 。
不管当前用户被假定成什么用户, 这个数都用来表示当前用户的标识号, 也可能使用 su 命令来达到假定的目的。
$EUID 并不一定与 $UID 相同。 - $FUNCNAME
当前函数的名字。(数组形式 $FUNCNAME[0] ) - $GLOBIGNORE
一个文件名的模式匹配列表,如果在通配(globbing)中匹配到的文件包含有这个列表中的某个文件, 那么这个文件将被从匹配到的结果中去掉。 - $GROUPS
目前用户所属的组。
这是一个当前用户的组 id 列表(数组), 与记录在 /etc/passwd 文件中的内容一样。
- $HOME
用户的 home 目录, 一般是 /home/username - $HOSTNAME
hostname 放在一个初始化脚本中, 在系统启动的时候分配一个系统名字。 然而, gethostname() 函数可以用来设置这个 Bash 内部变量 $HOSTNAME 。 - $HOSTTYPE
主机类型
就像 $MACHTYPE , 用来识别系统硬件。
- $IFS
内部域分隔符
这个变量用来决定 Bash 在解释字符串时如何识别域, 或者单词边界。
$IFS 默认为 空白(空格, 制表符,和换行符), 但这是可以修改的, 比如, 在分析逗号分隔的数据文件时, 就可以设置为逗号。 注意 $* 使用的是保存在 $IFS 中的第一个字符。 - $IGNOREEOF
忽略 EOF : 告诉 shell 在 log out 之前要忽略多少文件结束符(control-D)。 - $LC_COLLATE
常在 .bashrc 或 /etc/profile 中设置, 这个变量用来控制文件名扩展和模式匹配的展开顺序。 如果 $LC_COLLATE 设置得不正确的话, LC_COLLATE 会在文件名匹配(filename globbing)中产生不可预料的结果。 - $LC_CTYPE
这个内部变量用来控制通配(globbing)和模式匹配中的字符串解释。 - $LINENO
这个变量用来记录自身在脚本中所在的行号。 这个变量只有在脚本使用这个变量的时候才有意义,并且这个变量一般用于调试目的。 - $MACHTYPE
机器类型。标识系统的硬件。
- $OLDPWD
之前的工作目录(“OLD-print-working-directory”, 就是之前你所在的目录) - $OSTYPE
操作系统类型 - $PATH
可执行文件的搜索路径, 一般为 /usr/bin/, /usr/X11R6/bin/, /usr/local/bin, 等等。 - $PIPESTATUS
这个数组变量将保存最后一个运行的前台管道的退出状态码。 相当有趣的是, 这个退出状态码和最后一个命令运行的退出状态码并不一定相同。 - $PPID
进程的 $PPID 就是这个进程的父进程的进程 ID(pid)。 - $PROMPT_COMMAND
这个变量保存了在主提示符 $PS1 显示之前需要执行的命令。 - $PS1
这是主提示符, 可以在命令行中见到它。 - $PS2
第二提示符, 当你需要额外输入的时候, 你就会看到它。 默认显示 “>” 。 - $PS3
第三提示符, 它在一个 select 循环中显示 。 - $PS4
第四提示符, 当你使用 -x 选项来调用脚本时, 这个提示符会出现在每行输出的开头。 默认显示 “+” 。 - $PWD
工作目录(你当前所在的目录) - $REPLY
当没有参数变量提供给 read 命令的时候, 这个变量会作为默认变量提供给 read 命令。 也可以用于 select 菜单, 但是只提供所选择变量的编号, 而不是变量本身的值。 - $SECONDS
这个脚本已经运行的时间(以秒为单位)。 - $SHELLOPTS
shell 中已经激活的选项的列表, 这是一个只读变量。
- $SHLVL
Shell 级别, 就是 Bash 被嵌套的深度。 如果是在命令行中, 那么 $SHLVL 为 1, 如果在脚本中那么 $SHLVL 为 2 。 - $TMOUT
如果 $TMOUT 环境变量被设置为非零值 time 的话, 那么经过 time 秒后, shell 提示符将会超时。 这将会导致登出(logout)。 - $UID
用户 ID 号。当前用户的用户标识号, 记录在 /etc/passwd 文件中
位置参数
- $0, $1, $2, 等等。
位置参数, 从命令行传递到脚本, 或者传递给函数, 或者 set 给变量 - $#
命令行参数或者位置参数的个数 - $*
所有的位置参数都被看作为一个单词。
“$*” 必须被引用起来。 - $@
与 $* 相同, 但是每个参数都是一个独立的引用字符串, 这就意味着, 参数是被完整传递的, 并没有被解释或扩展。 这也意味着, 参数列表中每个参数都被看作为单独的单词。
“$@” 应该被引用起来。
其他的特殊参数
- $-
传递给脚本的标记(使用 set 命令)。 - $!
运行在后台的最后一个作业的 PID(进程 ID) - $_
这个变量保存之前执行的命令的最后一个参数的值 - $?
命令, 函数, 或者是脚本本身的退出状态码 - $$
脚本自身的进程 ID。 $$ 变量在脚本中经常用来构造 “唯一的” 临时文件名
(2) 操作字符串
Bash 所支持的字符串操作的数量多的令人惊讶。 但是不幸的是, 这些工具缺乏统一的标准。 一些是参数替换的子集, 而另外一些则受到 UNIX expr 命令的影响。 这就导致了命令语法的不一致, 还会引起冗余的功能, 但是这些并没有引起混乱。
字符串长度
语法:
示例:
${#var}
字符串长度 (变量$var得字符个数) 。 对于 array 来说, ${#array} 表示的是数组中第一个元素的长度。
例外情况:
- ${#*} 和 ${#@} 表示位置参数的个数。
- 对于数组来说, ${#array[*]} 和 ${#array[@]} 表示数组中元素的个数。
匹配字符串开头的子串长度
语法:
示例:
索引
语法:
示例:
提取子串
语法:
示例:
如果 $string 参数是 "*"
或 "@"
, 那么将会从 $position 位置开始提取 $length 个位置参数, 但是由于可能没有 $length 个位置参数了,那么就有几个位置参数就提取几个位置参数。
语法:
示例:
语法:
示例:
子串削除
语法:
示例:
语法:
示例:
子串替换
语法:
示例:
语法:
示例:
(3) 参数替换
处理和(或)扩展变量
- ${parameter}
与 $parameter 相同, 也就是变量 parameter 的值。 - ${parameter-default}, ${parameter:-default}
${parameter-default} – 如果变量 parameter 没被声明, 那么就使用默认值。
${parameter:-default} – 如果变量 parameter 没被设置, 那么就使用默认值。 - ${parameter=default}, ${parameter:=default}
${parameter=default} – 如果变量 parameter 没声明, 那么就把它的值设为 default。
${parameter:=default} – 如果变量 parameter 没设置, 那么就把它的值设为 default。
这两种形式基本上是一样的。 只有在变量 $parameter 被声明并且被设置为 null 值的时候, 才会引起这两种形式的不同。 - ${parameter+alt_value}, ${parameter:+alt_value}
${parameter+alt_value} – 如果变量 parameter 被声明了, 那么就使用 alt_value , 否则就使用 null 字符串。
${parameter:+alt_value} – 如果变量 parameter 被设置了, 那么就使用 alt_value , 否则就使用 null 字符串。
这两种形式绝大多数情况下都一样。 只有在 parameter 被声明并且设置为 null 值的时候, 多出来的这个: 才会引起这两种形式的不同。 - ${parameter?err_msg}, ${parameter:?err_msg}
${parameter?err_msg} – 如果 parameter 已经被声明, 那么就使用设置的值, 否则打印 err_msg 错误消息。
${parameter:?err_msg} – 如果 parameter 已经被设置, 那么就使用设置的值, 否则打印 err_msg 错误消息。
这两种形式绝大多数情况都是一样的。 和上边所讲的情况一样, 只有在 parameter 被声明并设置为 null 值的时候, 多出来的:才会引起这两种形式的不同。
(4)指定变量的类型: 使用 declare 或者 typeset
declare 或者 typeset 内建命令(这两个命令是完全一样的)允许指定变量的具体类型。 在某些编程语言中, 这是指定变量类型的一种很弱的形式。 declare 命令是从 Bash 2.0 之后才被引入的命令。 typeset 也可以用在 ksh 的脚本中。
declare/typeset 选项
- -r 只读
(declare -r var1 与 readonly var1 是完全一样的)
这和 C 语言中的 const 关键字一样, 都用来指定变量为只读。 如果你尝试修改一个只读变量的值,那么会产生错误信息。
- -i 整型
如果把一个变量指定为整型的话, 那么即使没有 expr 或者 let 命令, 也允许使用特定的算术运算。
- -a 数组
变量 indices 将被视为数组。
- -f 函数
如果在脚本中使用declare -f, 而不加任何参数的话, 那么将会列出这个脚本之前定义的所有函数。
如果在脚本中使用declare -f function_name这种形式的话, 将只会列出这个函数的名字。
- -x export
这句将会声明一个变量, 并作为这个脚本的环境变量被导出。
- -x var=$value
declare 命令允许在声明变量类型的同时给变量赋值。
示例:
(5)变量的间接引用
假设一个变量的值是第二个变量的名字。 那么我们如何从第一个变量中取得第二个变量的值呢? 比如,如果 a=letter_of_alphabet 并且 letter_of_alphabet=z , 那么我们能够通过引用变量 a 来获得 z 么? 这确实是可以做到的, 它被称为间接引用。 它使用 eval var1=\$$a 这种不平常的形式。
变量的间接引用到底有什么应用价值? 它给 Bash 添加了一种类似于 C 语言指针的功能, 比如, 在表格查找中的用法。
(6)$RANDOM: 产生随机整数
$RANDOM 是 Bash 的内部函数 (并不是常量), 这个函数将返回一个伪随机整数, 范围在 0 - 32767 之间。 它不应该被用来产生密匙。
(7)双圆括号结构
与 let 命令很相似, ((…)) 结构允许算术扩展和赋值。 举个简单的例子, a=$(( 5 + 3 )), 将把变量 “a” 设为 “5 + 3” , 或者 8 。 然而, 双圆括号结构也被认为是在 Bash 中使用 C 语言风格变量操作的一种处理机制。
☆