2.5 引用
2.5.0 引用
1.重要的作用就是保护命令行中的参数,但还是允许正在调用的程序来扩展它.
2.可以抑制 echo 命令的换行作用,echo "$(ls -l)"
2.5.1 引用变量
在一个双引号中直接使用变量名,一般都是没有问题的.它阻止了所有在引号中的特殊字符的
重新解释--包括变量名[2]--但是$,`和/除外.[3]保留$,作为特殊字符的意义.[4]如果在参
数列表中使用双引号,将使得双引号中的参数作为一个参数.
5.2 转义(/)
转义是一种引用单个字符的方法.一个具有特殊含义的字符前边放上一个转义符(/)就告诉shell
这个字符失去了特殊的含义.
对于特定的转义符的特殊的含义
在 echo 和sed 中所使用的
/n 意味着新的一行
/r 回车
/t tab 键
/v vertical tab(垂直tab),查前边的Ctl-K
/b backspace,查前边的Ctl-H
/a "alert"(如beep 或flash)
/0xx 转换成8 进制ASCII 解码,等价于oxx
echo使用-e 选项命令来打印转义符
echo -e "/v/v/v/v"
使用$'/X'结构,可代替-e 选项
echo $'/n' # 新行
/" 表达引号本身
echo "/"Hello/", he said." # "Hello", he said.
/$ 表达$号本身,跟在/$后的变量名,将不能扩展
echo "/$variable01" # 结果是$variable01
// 表达/号本身.
echo "//"
转义符也提供续行功能
echo hello/
world
2.6 退出和退出状态
exit #退出
echo $? #是否为0
2.7 tests
把[[ $a -lt $b ]]看作一个单独的元素,并且返回一个退出码.
((...))和let...结果也能够返回一个退出码
let "1<2" returns 0 (as "1<2" expands to "1")
7.2 文件测试操作
----------------
返回true 如果...
-e 文件存在
-a 文件存在
这个选项的效果与-e 相同.但是它已经被弃用了,并且不鼓励使用
-f file 是一个regular 文件(不是目录或者设备文件)
-s 文件长度不为0
-d 文件是个目录
-b 文件是个块设备(软盘,cdrom 等等)
-c 文件是个字符设备(键盘,modem,声卡等等)
-p 文件是个管道
-h 文件是个符号链接
-L 文件是个符号链接
-S 文件是个socket
-t 关联到一个终端设备的文件描述符
这个选项一般都用来检测是否在一个给定脚本中的 stdin[-t0]或[-t1]是一个终端
-r 文件具有读权限(对于用户运行这个test)
-w 文件具有写权限(对于用户运行这个test)
-x 文件具有执行权限(对于用户运行这个test)
-g set-group-id(sgid)标志到文件或目录上
如果一个目录具有 sgid 标志,那么一个被创建在这个目录里的文件,这个目录属于创建
这个目录的用户组,并不一定与创建这个文件的用户的组相同.对于workgroup 的目录
共享来说,这非常有用.见<<UNIX 环境高级编程中文版>>第58 页.
-u set-user-id(suid)标志到文件上
如果运行一个具有 root 权限的文件,那么运行进程将取得root 权限,即使你是一个普通
用户.[1]这对于需要存取系统硬件的执行操作(比如pppd 和cdrecord)非常有用.如果
没有 suid 标志的话,那么普通用户(没有root 权限)将无法运行这种程序.
见<<UNIX 环境高级编程中文版>>第58 页.
-rwsr-xr-t 1 root 178236 Oct 2 2000 /usr/sbin/pppd
对于设置了 suid 的文件,在它的权限标志中有"s".
-k 设置粘贴位,见<<UNIX 环境高级编程中文版>>第65 页.
对于"sticky bit",save-text-mode 标志是一个文件权限的特殊类型.如果设置了这
个标志,那么这个文件将被保存在交换区,为了达到快速存取的目的.如果设置在目录
中,它将限制写权限.对于设置了sticky bit 位的文件或目录,权限标志中有"t".
drwxrwxrwt 7 root 1024 May 19 21:26 tmp/
如果一个用户并不时具有 stick bit 位的目录的拥有者,但是具有写权限,那么用户只
能在这个目录下删除自己所拥有的文件.这将防止用户在一个公开的目录中不慎覆盖
或者删除别人的文件,比如/tmp(当然root 或者是目录的所有者可以随便删除或重命名
其中的文件).
-O 你是文件的所有者.
-G 文件的group-id 和你的相同.
-N 从文件最后被阅读到现在,是否被修改.
f1 -nt f2
文件 f1 比f2 新
f1 -ot f2
f1比f2 老
f1 -ef f2
f1和f2 都硬连接到同一个文件.
! 非--反转上边测试的结果(如果条件缺席,将返回true)
7.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"))
<= 小于等于(需要双括号),如:(("$a" <= "$b"))
> 大于(需要双括号),如:(("$a" > "$b"))
>= 大于等于(需要双括号),如:(("$a" >= "$b"))
字符串比较
= 等于,如:if [ "$a" = "$b" ]
== 等于,如:if [ "$a" == "$b" ],与=等价
注意:==的功能在[[]]和[]中的行为是不同的,如下:
1 [[ $a == z* ]] # 如果$a 以"z"开头(模式匹配)那么将为true
2 [[ $a == "z*" ]] # 如果$a 等于z*(字符匹配),那么结果为true
3
4 [ $a == z* ] # File globbing 和word splitting 将会发生
5 [ "$a" == "z*" ] # 如果$a 等于z*(字符匹配),那么结果为true
关于File globbing 是一种关于文件的速记法,比如"*.c"就是,再如~也是.
但是 file globbing 并不是严格的正则表达式,虽然绝大多数情况下结构比较像.
!= 不等于,如:if [ "$a" != "$b" ]
这个操作符将在[[]]结构中使用模式匹配.
< 小于,在ASCII 字母顺序下.如:
if [[ "$a" < "$b" ]]
if [ "$a" /< "$b" ]
注意:在[]结构中"<"需要被转义.
> 大于,在ASCII 字母顺序下.如:
if [[ "$a" > "$b" ]]
if [ "$a" /> "$b" ]
注意:在[]结构中">"需要被转义.
-z 字符串为"null".就是长度为0.
-n 字符串不为"null"
注意:
使用-n 在[]结构中测试必须要用""把变量引起来.使用一个未被""的字符串来使用! -z
或者就是未用""引用的字符串本身,放到[]结构中(见Example 7-6)虽然一般情况下可
以工作,但这是不安全的.习惯于使用""来测试字符串是一种好习惯.[1]
算术操作符
+ 加法
- 减法
* 乘法
/ 除法
** 幂运算
# Bash, version 2.02, introduced the "**" exponentiation operator.
let "z=5**3"
echo "z = $z" # z = 125
% 取模
bash$ expr 5 % 3
位操作符
<< 左移1 位(每次左移都将乘2)
<<= 左移几位,=号后边将给出左移几位
let "var <<= 2"就是左移2 位(就是乘4)
>> 右移1 位(每次右移都将除2)
>>= 右移几位
& 按位与
&= 按位与赋值
| 按位或
|= 按位或赋值
~ 按位非
! 按位否?
^ 按位异或XOR
^= 异或赋值
逻辑操作:
&& 逻辑与
|| 逻辑或
混杂操作:
, 逗号操作符
逗号操作符可以连接 2 个或多个算术运算.所有的操作都会被执行,但是只有最后一个
操作作为结果.
let "t1 = ((5 + 3, 7 - 1, 15 - 4))"
echo "t1 = $t1" # t1 = 11
数字常量
shell 脚本默认都是将数字作为10 进制数处理,除非这个数字某种特殊的标记法或前缀开头.
以0 开头就是8 进制.以0x 开头就是16 进制数.
# 16 进制表示:数字以'0x'或者'0X'开头
let "hex = 0x32"
echo "hexadecimal number = $hex" # 50
# 表达式的结果用10 进制表示.
9 变量重游
9.1 内部变量
$BASH
这个变量将指向 Bash 的二进制执行文件的位置.
bash$ echo $BASH
/bin/bash
$BASH_ENV
这个环境变量将指向一个 Bash 启动文件,这个启动文件将在调用一个脚本时被读取.
$BASH_SUBSHELL
这个变量将提醒 subshell 的层次,这是一个在version3 才被添加到Bash 中的新特性.
$BASH_VERSINFO[n]
记录 Bash 安装信息的一个6 元素的数组.与下边的$BASH_VERSION 很像,但这个更加详
细.
1 # Bash version info:
2
3 for n in 0 1 2 3 4 5
4 do
5 echo "BASH_VERSINFO[$n] = ${BASH_VERSINFO[$n]}"
6 done
7
8 # BASH_VERSINFO[0] = 3 # 主版本号
9 # BASH_VERSINFO[1] = 00 # 次版本号
10 # BASH_VERSINFO[2] = 14 # Patch 次数.
11 # BASH_VERSINFO[3] = 1 # Build version.
12 # BASH_VERSINFO[4] = release # Release status.
13 # BASH_VERSINFO[5] = i386-redhat-linux-gnu
$BASH_VERSION
安装在系统上的 Bash 的版本号.
bash$ echo $BASH_VERSION
3.00.14(1)-release
tcsh% echo $BASH_VERSION
BASH_VERSION: Undefined variable.
使用这个变量对于判断系统上到底运行的是那个 shll 来说是一种非常好的办法.$SHELL
有时将不能给出正确的答案.
$DIRSTACK
在目录栈中最上边的值(将受到pushd 和popd 的影响).
这个内建的变量与 dirs 命令是保持一致的,但是dirs 命令将显示目录栈的整个内容.
$EDITOR
脚本调用的默认编辑器,一般是vi 或者是emacs.
$EUID
"effective"用户ID 号.
当前用户被假定的任何 id 号.可能在su 命令中使用.
注意:$EUID 并不一定与$UID 相同.
$FUNCNAME
当前函数的名字.
1 xyz23 ()
2 {
3 echo "$FUNCNAME now executing." # xyz23 现在正在被执行.
4 }
5
6 xyz23
7
8 echo "FUNCNAME = $FUNCNAME" # FUNCNAME =
9 # 出了函数就变为Null 值了.
$GLOBIGNORE
一个文件名的模式匹配列表,如果在file globbing 中匹配到的文件包含这个列表中的
某个文件,那么这个文件将被从匹配到的文件中去掉.
$GROUPS
当前用户属于的组.
这是一个当前用户的组 id 列表(数组),就像在/etc/passwd 中记录的一样.
root# echo $GROUPS
0
root# echo ${GROUPS[1]}
1
root# echo ${GROUPS[5]}
6
$HOME
用户的 home 目录,一般都是/home/username(见Example 9-14)
$HOSTNAME
hostname 命令将在一个init 脚本中,在启动的时候分配一个系统名字.
gethostname()函数将用来设置这个$HOSTNAME 内部变量.(见Example 9-14)
$HOSTTYPE
主机类型
就像$MACHTYPE,识别系统的硬件.
bash$ echo $HOSTTYPE
$IGNOREEOF
忽略 EOF: 告诉shell 在log out 之前要忽略多少文件结束符(control-D).
$LINENO
这个变量记录它所在的 shell 脚本中它所在行的行号.这个变量一般用于调试目的.
$MACHTYPE
系统类型
提示系统硬件
bash$ echo $MACHTYPE
$PATH
指向 Bash 外部命令所在的位置,一般为/usr/bin,/usr/X11R6/bin,/usr/local/bin 等.
当给出一个命令时,Bash 将自动对$PATH 中的目录做一张hash 表.$PATH 中以":"分隔的
目录列表将被存储在环境变量中.一般的,系统存储的$PATH 定义在/ect/processed 或
~/.bashrc.
$PPID
一个进程的$PPID 就是它的父进程的进程id(pid).[1]
$PROMPT_COMMAND
这个变量保存一个在主提示符($PS1)显示之前需要执行的命令.
$PWD
工作目录(你当前所在的目录).
$TMOUT
如果$TMOUT 环境变量被设置为一个非零的时间值,那么在过了这个指定的时间之后,
shell提示符将会超时,这会引起一个logout.
$UID
用户 ID 号.
当前用户的 id 号,在/etc/passwd 中记录.
这个值不会因为用户使用了 su 命令而改变.$UID 是只读变量,不容易在命令行或者是脚
本中被修改,并且和内建的id 命令很相像.
位置参数
$0, $1, $2,等等...
位置参数,从命令行传递给脚本,或者是传递给函数.或者赋职给一个变量.
$#
命令行或者是位置参数的个数.
$*
所有的位置参数,被作为一个单词.
注意:"$*"必须被""引用.
$@
与$*同义,但是每个参数都是一个独立的""引用字串,这就意味着参数被完整地传递,
并没有被解释和扩展.这也意味着,每个参数列表中的每个参数都被当成一个独立的
单词.
注意:"$@"必须被引用.
$!
在后台运行的最后的工作的 PID
$-
传递给脚本的 falg
9.2 操作字符串
从字符串开始的位置匹配子串的长度
expr match "$string" '$substring'
$substring 是一个正则表达式
expr "$string" : '$substring'
$substring 是一个正则表达式
expr index $string $substring
匹配到子串的第一个字符的位置
stringZ=abcABC123ABCabc
echo `expr index "$stringZ" C12 #6
提取子串
${string:position}
在 string 中从位置$position 开始提取子串.
如果$string 为"*"或"@",那么将提取从位置$position 开始的位置参数,[1]
${string:position:length}
在 string 中从位置$position 开始提取$length 长度的子串
${string:(-position)}
反向提取子串
expr substr $string $position $length
在 string 中从位置$position 开始提取$length 长度的子串.
子串削除
${string#substring}
从$string 的左边截掉第一个匹配的$substring
${string##substring}
从$string 的左边截掉最后一个个匹配的$substring
9.2.1 awk操作字符串
awk的第一个字符是从'1'开始记录的.
9.4 指定类型的变量:declare 或者typeset
declare/typeset 选项
-r 只读
declare -r var1
(declare -r var1与readonly var1 是完全一样的)
-i 整形
declare -i number
-a 数组
1 declae -a indices
变量 indices 将被视为数组
-f 函数
declare -f
如果使用 declare -f 而不带参数的话,将会列出这个脚本中之前定义的所有函数.
declare -f function_name
如果使用 declare -f function_name 这种形式的话,将只会列出这个函数的名字
-x export
declare -x var3
这种使用方式,将会把var3 export 出来.
9.5 变量的间接引用
使用符号:eval var1=/$$var2.
9.6 $RANDOM: 产生随机整数
$RANDOM 是Bash 的内部函数(并不是常量),这个函数将返回一个范围在0 - 32767 之间的一个伪
随机整数.它不应该被用来产生密匙.
9.7 双圆括号结构
((...))与let 命令很像,允许算术扩展和赋值.举个简单的例子a=$(( 5 + 3 )),将把a 设为
"5+3"或者8.然而,双圆括号也是一种在Bash 中允许使用C 风格的变量处理的机制
[未完]