最近在学习shell时发现虽然我经常使用test和case,但是它们仍然有许多不为人知的秘密。

  首先来看test

  • 两个test 

在linux中有两个test命令,一个是作为shell的内置命令,它是一个程序;另一个一般是磁盘的/usr/bin/test命令,但他一般不会被调用,因为首先找到的是shell的内置命令。

       此外,test的另一个名称是[ 。 当调用[ 时,它需要一个] 作为其参数,但是如果不使用],[ 也能起到同样的作用。

[root@localhost ~]# type test; type [
test is a shell builtin
[ is a shell builtin
[root@localhost ~]# which test; which [
/usr/bin/test
/usr/bin/[

从中可以看到,在shell中,[是程序,而]只是[所需要的参数。

  •  常用的测试标志

  1. -e   是否存在;

  2. -s    是否存在且不为空;

  3. -f    是否为常规文件;

  4. -b    是否为块设备(块设备的驱动对象是像磁盘这样的按块进行操作的设备);

  5. -c    是否为字符设备(字符设备的驱动对象可以向其读写字符的设备,如终端 );

  6. -L(-h)    是否为符号链接;

  7. -(rwx)    是否具有相应的权限,

  8. -O(-G)    是否属于当前用户和/或组ID;

  9. -u(-g)    是否具有suid或sgid(suid位与sgid位允许程序以文件所属用户(或组)的身份运行,而不一定是运行程序的用户(组) );

[root@localhost ~]# cat suid.sh
#!/bin/bash
while read -p "What file do you want to test? :" filename
do
if [ ! -e "$filename" ]; then
echo "The file does not exists."
continue
fi
#Okey, the file exists
ls -ld $filename
if [ -u $filename ]; then
echo " $filename will run as user \"`stat --printf=%U $filename`\""
fi
if [ -g $filename ]; then
echo "$filename will run as group \"`stat --printf=%G $filename`\""
fi
done


 10. -N标志检测文件自从上次读取之后是否被修改。

[root@localhost ~]# echo hello > /tmp/myfile.log
[root@localhost ~]# echo hello world >> /tmp/myfile.log
[root@localhost ~]# cat watchfile.sh
#!/bin/bash
GAP=10          # how long to wait
LOGFILE=$1      # file to log to
# Get the current length of the file.
len=`wc -l $LOGFILE | awk '{ print $1 }'`
echo "Current size is $len lines"
while :
do
if [ -N $LOGFILE ]; then
echo "`date`: New entries in $LOGFILE:"
newlen=`wc -l $LOGFILE | awk '{ print $1 }'`
newlines=`expr $newlen - $len`
tail -$newlines $LOGFILE
len=$newlen
fi
sleep $GAP
done
  • 文件比较测试

  -ef比较两个文件是否为同一文件系统中相同inode节点的硬链接。


[root@localhost ~]# cat equalfile.sh
#!/bin/bash
file1=$1
file2=$2
ls -il $file1 $file2
if [ $file1 -ef $file2 ]; then
echo "$file1 is the same file as $file2"
else
echo "$file1 is not the same file as $file2"
diff -q $file1 $file2
if [ "$?" -eq "0" ]; then
echo "However, their contents are identical."
fi
fi
[root@localhost ~]# echo testing > file1
[root@localhost ~]# ln file1 file2
[root@localhost ~]# echo testing > file3
[root@localhost ~]# ls -il file?
131679 -rw-r--r--. 2 root root 8 Nov 28 05:42 file1
131679 -rw-r--r--. 2 root root 8 Nov 28 05:42 file2
131995 -rw-r--r--. 1 root root 8 Nov 28 05:42 file3
[root@localhost ~]# sh equalfile.sh file1 file2
131679 -rw-r--r--. 2 root root 8 Nov 28 05:42 file1
131679 -rw-r--r--. 2 root root 8 Nov 28 05:42 file2
file1 is the same file as file2
[root@localhost ~]# sh equalfile.sh file1 file3
131679 -rw-r--r--. 2 root root 8 Nov 28 05:42 file1
131995 -rw-r--r--. 1 root root 8 Nov 28 05:42 file3
file1 is not the same file as file3
However, their contents are identical.
[root@localhost ~]# echo something > file4
[root@localhost ~]# sh equalfile.sh file1 file4
131679 -rw-r--r--. 2 root root  8 Nov 28 05:42 file1
132003 -rw-r--r--. 1 root root 10 Nov 28 05:45 file4
file1 is not the same file as file4
Files file1 and file4 differ
  • 字符串比较测试

    可以测试字符串是否相等,以及按照字母表测试某个字符串是否排在另一个前面。

需要注意的是: <和>只能在复合命令[[ ... ]]中起作用;

                        ==和=!可以在单方括号和双括号中起作用;

                        =只能在单括号中起作用;

导致这种复杂性的原因是为了在添加更加强大的[[命令的同时,保持与Bourne shell的兼容性。


参数:
-z  测试在字符串长度为0时返回真,-n 在字符串长度非零时返回真。

因为测试的字符串可能为空,所以需要用引号将变量名引用起来:否则[ -z $input ]就会成为[ -z   ],这是错的,只有[ -z "$input" ]才是合法的。

[root@localhost ~]# cat alnum.sh
#!/bin/bash
if [ "$1" = "$2" ]; then
echo "$1 is the same as $2"
else
echo "$1 is not the same as $2"
if [[ "$1" < "$2" ]]; then
echo "$1 comes before $2"
else
echo "$1 comes after $2"
fi
fi
[root@localhost ~]# sh alnum.sh apples bananas
apples is not the same as bananas
apples comes before bananas
[root@localhost ~]# sh alnum.sh oranges oranges
oranges is the same as oranges
  • 正则表达式测试

参数: =~  它将右边当作扩展的正则表达式,故可以通过[[ $pkgname =~ .*\.deb ]] 来查找匹配模式*.deb的文件名。注意要使用[[ ... ]]

[root@localhost ~]# cat identify_pkgs.sh
#!/bin/bash
for pkg in pkgs/*
do
pkgname=`basename $pkg`
echo $pkgname
if [[ $pkgname =~ (.+)_(.*)_(.*)\.deb ]]; then
echo "Package ${BASH_REMATCH[1]} Version ${BASH_REMATCH[2]} is a"
echo " Debian package for the ${BASH_REMATCH[3]} architecture."
echo
elif [[ $pkgname =~ (.+)-(.+)\.(.*)\.rpm ]]; then
echo "Package ${BASH_REMATCH[1]} Version ${BASH_REMATCH[2]} is an"
echo " RPM for the ${BASH_REMATCH[3]} architecture."
echo
else
echo "File \"$pkgname\" does not appear to match the"
echo " standard .deb or .rpm naming conventions."
fi
done
[root@localhost ~]# ls pkgs/
dbus-x11_1.2.24-4_and64.deb            gnome-desktop-1.023.x86_64.rpm
firmware-linux-free_2.6.32-29_all.deb  libgnomeprint-2.18.6-2.6.x86_64.rpm
[root@localhost ~]# sh identify_pkgs.sh
dbus-x11_1.2.24-4_and64.deb
Package dbus-x11 Version 1.2.24-4 is a
Debian package for the and64 architecture.
firmware-linux-free_2.6.32-29_all.deb
Package firmware-linux-free Version 2.6.32-29 is a
Debian package for the all architecture.
gnome-desktop-1.023.x86_64.rpm
Package gnome-desktop Version 1.023 is an
RPM for the x86_64 architecture.
libgnomeprint-2.18.6-2.6.x86_64.rpm
Package libgnomeprint-2.18.6 Version 2.6 is an
RPM for the x86_64 architecture.
  • 数值测试

常用参数: -eq   相等返回真;                        -ne    不相等返回真;

               -lt     小于返回真;                        -gt     大于返回真;

               -le   小于等于返回真;                  -ge     大于等于返回真;

[root@localhost ~]# cat numberguess.sh
#!/bin/bash
MAX=50
guess=-1
let answer=($RANDOM %$MAX)
let answer+=1
ceiling=$MAX
floor=0
guesses=0
while [ "$guess" -ne "$answer" ]
do
echo "The magic number is between $floor and $ceiling."
echo -en " Make your guess:"
read guess
guesses=`expr $guesses + 1`
if [ "$guess" -lt "$answer" ]; then
echo "$guess is too low"
if [ "$guess" -ge "$floor" ]; then
floor=`expr $guess + 1`
fi
fi
if [ "$guess" -gt "$answer" ]; then
echo "$guess is too high"
if [ "$guess" -lt "$ceiling" ]; then
ceiling=`expr $guess - 1`
fi
fi
done
echo "You got it in $guesses guesses!"
[root@localhost ~]# sh numberguess.sh
The magic number is between 0 and 50.
Make your guess:25
25 is too high
The magic number is between 0 and 24.
Make your guess:12
12 is too low
The magic number is between 13 and 24.
Make your guess:18
18 is too high
The magic number is between 13 and 17.
Make your guess:15
15 is too low
The magic number is between 16 and 17.
Make your guess:16
16 is too low
The magic number is between 17 and 17.
Make your guess:17
You got it in 6 guesses!
  • 组合测试

使用&&和||操作符来将测试组合

[root@localhost ~]# wc -l /etc/hosts || echo wc failed to read /etc/hosts
2 /etc/hosts
[root@localhost ~]# wc -l /etc/hosts.bak || echo wc failed to read /etc/hosts
wc: /etc/hosts.bak: No such file or directory
wc failed to read /etc/hosts
[root@localhost ~]# wc -l /etc/hosts | grep "^3" || echo /etc/hosts is 3 lines file
/etc/hosts is 3 lines file
[root@localhost ~]# wc -l /etc/hosts | grep "^2" && echo /etc/hosts is 2 lines file
2 /etc/hosts
/etc/hosts is 2 lines file


再来看case

对于case很多人都了解,但是关于用 ;;  ,  ;;& 和 ;&来终止语句,你了解吗?

;;     表示不再执行其他语句;

;;&   表示还要匹配接下来的所有模式;

;&    表示接下来的模式已经匹配;

[root@localhost ~]# cat case2.sh
#!/bin/bash
read -p "Which city are you closest to?:" city
case $city in
"New York"|London|Paris|Tokyo) echo "That is a capital city" ;;&
Chicago|Detroit|"New York"|Washington) echo "you are in the USA" ;;
London|Edinburgh|Cardiff|Dublin) echo "you are in the United Kingdom" ;;
"Ramsey Street") echo "G'Day Netghbour!" ;&
Melbourne|Canberra|Sydney) echo "you are in australai" ;;
Paris) echo "you are in France" ;;
Tokyo) echo "you are in Japan" ;;
N*) echo "your word begins with N but is not New York!" ;;
*) echo "I'm sorry, I don't konw anything about $city" ;;
esac
[root@localhost ~]# sh case2.sh
Which city are you closest to?:London
That is a capital city
you are in the United Kingdom
[root@localhost ~]# sh case2.sh
Which city are you closest to?:Ramsey street
I'm sorry, I don't konw anything about Ramsey street
[root@localhost ~]# sh case2.sh
Which city are you closest to?:Ramsey Street
G'Day Netghbour!
you are in australai