您的位置:首页 > 运维架构 > Shell

高级Bash脚本编程指南(7):文件测试操作符

2013-05-06 14:08 141 查看
高级Bash脚本编程指南(7):文件测试操作符
成于坚持,败于止步

如果下面的条件成立将会返回真.

-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权限来运行这个文件. [1] 这对于需要访问系统硬件的执行程序(比如pppd和cdrecord)非常有用. 如果没有suid标志的话, 这些二进制执行程序是不能够被非root用户调用的.对于设置了suid标志的文件, 在它的权限列中将会以s表示.其实这个权限在最初说文件权限的文章中已经说过了,比较特殊的一种权限

-k

设置粘贴位,对于"粘贴位"的一般了解, save-text-mode标志是一个文件权限的特殊类型. 如果文件设置了这个标志, 那么这个文件将会被保存到缓存中, 这样可以提高访问速度. [2] 粘贴位如果设置在目录中, 那么它将限制写权限. 对于设置了粘贴位的文件或目录, 在它们的权限标记列中将会显示t.

-O

判断你是否是文件的拥有者

-G

文件的group-id是否与你的相同

-N

从文件上一次被读取到现在为止, 文件是否被修改过

f1 -nt f2

文件f1比文件f2新

f1 -ot f2

文件f1比文件f2旧

f1 -ef f2

文件f1和文件f2是相同文件的硬链接

!

"非" -- 反转上边所有测试的结果(如果没给出条件, 那么返回真).

这里只是先把一个实例贴在这里,暂且不做任何分析,因为现在去分析这个程序还有点为时尚早:

#!/bin/bash
#一个纯粹的shell脚本用来找出那些断掉的符号链接文件并且输出它们所指向的文件.
#以便于它们可以把输出提供给xargs来进行处理 :)
#比如. xxx.sh /somedir /someotherdir|xargs rm
#下边的方法, 不管怎么说, 都是一种更好的办法:
#find "somedir" -type l -print0|\
#xargs -r0 file|\
#grep "broken symbolic"|
#sed -e 's/^\|: *broken symbolic.*$/"/g'
#但这不是一个纯粹的bash脚本, 最起码现在不是.
#注意: 谨防在/proc文件系统和任何死循环链接中使用!

#如果没有参数被传递到脚本中, 那么就使用
#当前目录. 否则就是用传递进来的参数作为目录
#来搜索.
[ $# -eq 0 ] && directorys=`pwd` || directorys=$@

#编写函数linkchk用来检查传递进来的目录或文件是否是链接, 
#并判断这些文件或目录是否存在. 然后打印它们所指向的文件.
#如果传递进来的元素包含子目录, 
#那么把子目录也放到linkcheck函数中处理, 这样就达到了递归的目的.
linkchk () {
	for element in $1/*; do
	[ -h "$element" -a ! -e "$element" ] && echo \"$element\"
	[ -d "$element" ] && linkchk $element
	# 当然, '-h'用来测试符号链接, '-d'用来测试目录.
	done
}

#把每个传递到脚本的参数都送到linkchk函数中进行处理, 
#检查是否有可用目录. 如果没有, 那么就打印错误消息和
#使用信息.
for directory in $directorys; do
	if [ -d $directory ]
		then linkchk $directory
	else 
		echo "$directory is not a directory"
		echo "Usage: $0 dir1 dir2 ..."
	fi
done
exit 0


其他比较操作符

二元比较操作符用来比较两个变量或数字. 注意整数比较与字符串比较的区别.

整数比较

-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" ]

与=等价,不过,==比较操作符在双中括号对和单中括号对中的行为是不同的.我们看看实例,理解一下:

#!/bin/bash
a=abcdef
if [[ $a == ab* ]]
then
	echo "result1: true"
else
	echo "result1: false"
fi

if [[ $a == "ab*" ]]
then
	echo "result2: true"
else
	echo "result2: false"
fi

if [ $a == abcdef ]
then
	echo "result3: true"
else
	echo "result3: false"
fi

if [ $a == "ab*" ]
then
	echo "result4: true"
else
	echo "result4: false"
fi

if [ $a == ab* ]
then
	echo "result5: true"
else
	echo "result5: false"
fi
exit 0
结果:

root@ubuntu:~/resource/shell-study/0427-2013# ./test2
result1: true
result2: false
result3: true
result4: false
result5: false
分析:

1.只要前两个字符为“ab”就为true

2.意思是变量a只有是“ab*”这三个字符时跟后面比较的字符完全一致才为true

3.就是完全比配的情况,返回肯定是true

4.和第二个是一样的

5.这个就是比较特殊的情况,我的理解是在单【】中文字扩展匹配和单词分割有效,所以不能采用这么模糊比较的方法,模糊比较必须使用双【【】】

!=

不等号

if [ "$a" != "$b" ]

这个操作符将在[[ ... ]]结构中使用模式匹配.

<

小于, 按照ASCII字符进行排序

if [[ "$a" < "$b" ]]

if [ "$a" \< "$b" ]

注意"<"使用在[ ]结构中的时候需要被转义.

>

大于, 按照ASCII字符进行排序

if [[ "$a" > "$b" ]]

if [ "$a" \> "$b" ]

注意">"使用在[ ]结构中的时候需要被转义.

-z

字符串为"null", 意思就是字符串长度为零

-n

字符串不为"null".当-n使用在中括号中进行条件测试的时候, 必须要把字符串用双引号引用起来. 如果采用了未引用的字符串来使用! -z, 甚至是在条件测试中括号中只使用未引用的字符串的话, 一般也是可以工作的, 然而, 这是一种不安全的习惯. 习惯于使用引用的测试字符串才是正路.

实例一

#!/bin/bash
a=4
b=5
#  这里的"a"和"b"既可以被认为是整型也可被认为是字符串. 
#  这里在算术比较与字符串比较之间是容易让人产生混淆, 因为Bash变量并不是强类型的.
#  Bash允许对于变量进行整形操作与比较操作.但前提是变量中只能包含数字字符.
#  不管怎么样, 还是要小心. 
if [ "$a" -ne "$b" ]
then
	echo "$a is not equal to $b"
	echo "(arithmetic comparison)"
fi

if [ "$a" != "$b" ]
then
	echo "str($a) is not equal to str($b)."
	echo "(string comparison)"
	#     "4"  != "5"
	# ASCII 52 != ASCII 53
fi
exit 0
结果:

root@ubuntu:~/resource/shell-study/0427-2013# ./test3 
4 is not equal to 5
(arithmetic comparison)
str(4) is not equal to str(5).
(string comparison)
-ne比较的是两个整数,!=比较的是两个字符串

针对判断字符串是否为空,还是要特别说一下吧,比较特殊:

#!/bin/bash

# -n 
if [ -n $string1 ];then
	echo "String \"string1\" is not null."
else
	echo "String \"string1\" is null."
fi

#This is the best way
if [ -n "$string2" ];then
	echo "String \"string2\" is not null."
else
	echo "String \"string2\" is null."
fi

if [ $string3 ];then
	echo "String \"string3\" is not null."
else
	echo "String \"string3\" is null."
fi

string4=initialize
if [ $string4 ];then
	echo "String \"string4\" is not null."
else
	echo "String \"string4\" is null."
fi

string5="a = b"
if [ $string5 ];then
	echo "String \"string5\" is not null."
else
	echo "String \"string5\" is null."
fi

string6="a = b"
if [ "$string6" ];then
	echo "String \"string6\" is not null."
else
	echo "String \"string6\" is null."
fi

exit 0

结果:

root@ubuntu:~/resource/shell-study/0506-2013# ./test1.sh 
String "string1" is not null.
String "string2" is null.
String "string3" is null.
String "string4" is not null.
String "string5" is null.
String "string6" is not null.
分析:

string1和string2同是未定义的string,为什么string1判断是非null,而string2判断就是null,对string1的判断明显是错误的,对string的操作加上“”是很有必要的,sting5和string6的比较同样说明了这一点

继续看一个实例:

#!/bin/bash

NOARGS=65
NOTFOUND=66
NOTTXT=67

if [ $# -eq 0 ];then
	echo "Usage: `basename $0` no filename!" >&2
	exit $NOARGS
fi

FILENAME=$1

if [ ! -f "$FILENAME" ];then
	echo "File $FILENAME not found!" >&2
	exit $NOTFOUND
fi

if [ ${FILENAME##*.} != "txt" ];then
	echo "File $1 is not a txt file!" >&2
	exit $NOTTXT
fi

cat $1 | more
exit 0


上面几个判断条件使用方法是重点关注的地方:

1.判断参数个数为0退出

2.先判断FILENAME是否为文件,再取反,即不是文件或找不到文件退出

3.判断文件后缀名是不是“.txt”,不是则退出

结果:

root@ubuntu:~/resource/shell-study/0506-2013# ls
file.txt  file.zip  test1.sh  test2.sh
root@ubuntu:~/resource/shell-study/0506-2013# ./test2.sh 
Usage: test2.sh no filename!
root@ubuntu:~/resource/shell-study/0506-2013# ./test2.sh file
File file not found!
root@ubuntu:~/resource/shell-study/0506-2013# ./test2.sh file.zip 
File file.zip is not a txt file!
root@ubuntu:~/resource/shell-study/0506-2013# ./test2.sh file.txt 
total 7476
-rwxr-xr-x 1 root root  818232 2010-04-18 18:51 bash
-rwxr-xr-x 1 root root   30200 2010-02-08 02:54 bunzip2
-rwxr-xr-x 1 root root 1269432 2010-04-22 13:04 busybox
-rwxr-xr-x 1 root root   30200 2010-02-08 02:54 bzcat
lrwxrwxrwx 1 root root       6 2012-11-30 05:12 bzcmp -> bzdiff
-rwxr-xr-x 1 root root    2140 2010-02-08 02:54 bzdiff
lrwxrwxrwx 1 root root       6 2012-11-30 05:12 bzegrep -> bzgrep
-rwxr-xr-x 1 root root    4874 2010-02-08 02:54 bzexe
lrwxrwxrwx 1 root root       6 2012-11-30 05:12 bzfgrep -> bzgrep
-rwxr-xr-x 1 root root    3642 2010-02-08 02:54 bzgrep
-rwxr-xr-x 1 root root   30200 2010-02-08 02:54 bzip2
-rwxr-xr-x 1 root root    9580 2010-02-08 02:54 bzip2recover
lrwxrwxrwx 1 root root       6 2012-11-30 05:12 bzless -> bzmore
-rwxr-xr-x 1 root root    1297 2010-02-08 02:54 bzmore
-rwxr-xr-x 1 root root   50820 2010-03-04 19:29 cat
-rwxr-xr-x 1 root root   54956 2010-03-04 19:29 chgrp
-rwxr-xr-x 1 root root   50836 2010-03-04 19:29 chmod
-rwxr-xr-x 1 root root   54964 2010-03-04 19:29 chown
-rwxr-xr-x 1 root root    9632 2010-03-11 23:05 chvt
-rwxr-xr-x 1 root root   96140 2010-03-04 19:29 cp
-rwxr-xr-x 1 root root  114448 2010-03-04 19:25 cpio
-rwxr-xr-x 1 root root   83888 2010-04-01 12:22 dash
-rwxr-xr-x 1 root root   63128 2010-03-04 19:29 date
-rwxr-xr-x 1 root root    9672 2010-03-30 08:07 dbus-cleanup-sockets
-rwxr-xr-x 1 root root  292728 2010-03-30 08:07 dbus-daemon
-rwxr-xr-x 1 root root    5524 2010-03-30 08:07 dbus-uuidgen
-rwxr-xr-x 1 root root   54996 2010-03-04 19:29 dd
-rwxr-xr-x 1 root root   67316 2010-03-04 19:29 df
-rwxr-xr-x 1 root root  104528 2010-03-04 19:29 dir
-rwxr-xr-x 1 root root    5524 2010-03-22 10:51 dmesg
-rwxr-xr-x 1 root root   13824 2010-03-10 11:12 dnsdomainname
root@ubuntu:~/resource/shell-study/0506-2013#


最后提一下逻辑判断方法

-a

逻辑与

exp1 -a exp2 如果表达式exp1和exp2都为真的话, 那么结果为真.

-o

逻辑或

exp1 -o exp2 如果表达式exp1和exp2中至少有一个为真的话, 那么结果为真.

这与Bash中的比较操作符&&和||非常相像, 但是这个两个操作符是用在双中括号结构中的.

比如用在【【】】中 [[ condition1 && condition2 ]]

-o和-a操作符一般都是和test命令或者是单中括号结构一起使用的, if [ "$exp1" -a "$exp2" ]


先到这里了,O(∩_∩)O~

我的专栏地址:http://blog.csdn.net/column/details/shell-daily-study.html
待续。。。。。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: