1、和let一样,[[ ]]也是一个bash命令,但是sh没有的。 下次对于sh: XXX:not found 可以通过把sh换成bash试试
2、关于NULL的if。od探究
对于NULL=040时候,NULL is false for if
对于a=040,即变量是NULL时候 if [ -n "$a" ] 也是false.(对于字符串 -n表示字符串长度>0return true,而NULL代表字符串长度=0)
但是对于if [ -n $a] 相当于if [ -n ] 缺少了string,这两个的返回值都是true ,虽然我觉得这种语法是错误的。
总结if:
if [ ... ]
...所在的测试条件:
1、对于任何单独的数字返回都是true
2、对于任何的字符串返回都是true
3、对于直接输入的NULL(即space ,tab),返回为false
4、对于变量=NULL if[ 变量 ],返回为false
5、对于变量=NULL,if[ "变量"],返回为false
5、对于if [ -n "$变量名(=NULL)" ],返回是false
6、对于if [ - n ]返回是true,或者说if [ -n "string" ]中的string必须加"",这是一个非常好的习惯!当然if [ "string" ]也可以判断string 是否为空,
就像Stephane Chazelas所指出的,
# if [ $string1 ] 只有一个参数, "]"
# if [ "$string1" ] 有两个参数, 一个是空的"$string1", 另一个是"]"
#一个足智多谋的例子:string1="a = b"对于if [ $string1 ]将会显示出错!
7、对于if[ $false ]和if[ "$false" ] 返回是false, 对于if [ false ]相当于2是true
如果if和then在条件判断的同一行上的话, 必须使用分号来结束if表达式. if和then都是关键字. 关键字(或者命令)如果作为表达式的开头, 并且如果想在同一行上再写一个新的表达式的话, 那么必须使用分号来结束上一句表达式 。
3、(( 1 / 0 )) 2>/dev/null
文件描述符我们常见的就是系统预留的0,1和2这三个,他们的意义分别有如下对应关系:
- 0 —— stdin(标准输入)
- 1 —— stdout (标准输出)
- 2 —— stderr (标准错误)
/dev/null是一个特殊的设备文件,这个文件接收到的任何数据都会被丢弃。因此,null这个设备通常也被成为位桶(bit bucket)或黑洞。
深入关于重定向:
在Xsession中有这么一段:
xmessage > /dev/null 2>&1
首先回顾一下重定向:
1、command 1>filename相当于(command)1>filename相当于command >filename
2、command 1>/dev/null 2>&1 ,这里&
相当于等效于标准输出。重定向command的stderr到stdout中。
3、command &>filename 重定向command的stdout和stderr到filename中.
4、command >&2 重定向command的stdout到stderr中.
5、scriptname >>filename 把scriptname的输出追加到文件filename中. 如果filename不存在的话,将会被创建.
6、[i]<>filename 打开文件filename用来读写, 并且分配文件描述符i给这个文件. 如果filename不存在, 这个文件将会被创建。
举个例子:
//test.sh
#!/bin/sh
t
date
这里的"t"是没有这个命令的,所以会输出stderr。
执行./test.sh > res1.log
结果为
我们发现stderr并没有被重定向到res1.log中,stderr被打印到了屏幕上。这也进一步证明了上面说的./test.sh > res1.log
等价于./test.sh 1>res1.log
执行./test.sh>res2.log 2>&1
结果为两句都输出到res2.log。
这里的2 >&1是吧stderr重定向到stdout然后两句一次重定向到res2.log。
command >filename 2>filename 与command >a 2>&1的区别:
还是上面的例子稍微改动一下:
//test.sh
#!/bin/sh
t
date
这里的"t"是没有这个命令的,所以会输出stderr。
执行./test.sh >res1.log 2>&1
结果res1.log中输入为./test.sh:line 2 : t:command not found
The Jul 5 22:39:38 CST 2018
执行./test.sh >res1.log 2>res1.log
结果res1.log中输入为The Jul 5 22:39:38 CST 2018
d not found
为什么呢?
两者的区别在于区别就在于前者只打开一次文件a,后者会打开文件两次,并导致stderr被stdout覆盖。&1的含义就可以理解为用标准输出的引用,引用的就是重定向标准输出产生打开的a。从IO效率上来讲,command 1>a 2>&1比command 1>a 2>a的效率更高。
注意:1、这里是stderr被stdout覆盖,很显然,这个打印中 sdterr并没有被全部覆盖,而且stdout出现在了stderr上面。这里的命令应该是从右向左读的。
2、如果把这个命令行参数改成./test.sh >>res1.log 2>res1.log,那么这个的结果和./test.sh >>res1.log 2>&1是一样的,但是实际情况还是有两次打开文件的区别。其中>>是追加输入的意思。
4、操作符
4.1、下面条件成立返回为真:
-e exist
-f 一般文件
-s size不为0
-d direction
-b block
-c charecter
-p pipe
-h 符号链接
-L 符号链接
-s socket
-t 文件描述符被关联到一个终端设备上
-r read
-w write
-x exec
-nt f1 new
-ot f1 old
-ef f1和f2是相同文件的硬链接
4.2、其他比较操作符
整数比较:
-eq 等于 if [ "$a" -eq "$b" ]
-另外还有:ne 不等于
-gt 大于
-ge 大于等于
-lt 小于
-le 小于等于
< 小于(在双括号里使用)---> (( "$a"< "$b" ))
另外还有:<= ,> ,>=
字符串比较
= 和==等价 if ["$a"=="$b" ]
!=不等号, if [ "$a"!="$b" ],这个操作符在[[ ... ]]结构中使用表示模式匹配
< 小于,按照ASCII字符进行排序
> 大于,在if [ "$a" > "$b" ] >在[]中需要转义
-z 字符串为"null",字符串长度为0
-n 字符串不为"null"
4.3、模式匹配
[[ $a==z* ]] #如果$a以"z"开头(模式匹配)那么结果为真
[[ $a=="z*" ]] #如果$a与z*相等,那么结果为真
4.4、逻辑与或
-a 逻辑与 exp1 -a exp2。两个都为真,那么结果为真
-o 逻辑或 exp1 -o exp2。一个为真就是真。
这与bash中的&& ||比较像,但是&& ||用于[[ ... ]]中双括号结构中
if [ "$exp1" -a "$exp2" ]
注:
1、在一个混合测试时,即使使用引用的变量字符串也可能还不够,如果$string为空的话,[ -n "$string" -o "$a"="$b" ]可能出错。安全的做法是附加一个额外的字符给可能的空变量,[ "x$string" != x -o "x$a"="x$b" ](“ x”字符是可以相互抵消的)
最后抄一个例子:
#!/bin/bash # zmore #使用'more'来查看gzip文件 NOARGS=65 NOTFOUND=66 NOTGZIP=67 if [ $# -eq 0 ] # 与if [ -z "$1" ]效果相同 # (译者注: 上边这句注释有问题), $1是可以存在的, 可以为空, 如: zmore "" arg2 arg3 then echo "Usage: `basename $0` filename" >&2 # 错误消息输出到stderr. exit $NOARGS # 返回65作为脚本的退出状态的值(错误码). fi filename=$1 if [ ! -f "$filename" ] # 将$filename引用起来, 这样允许其中包含空白字符. then echo "File $filename not found!" >&2 # 错误消息输出到stderr. exit $NOTFOUND fi if [ ${filename##*.} != "gz" ] # 在变量替换中使用中括号结构. then echo "File $1 is not a gzipped file!" exit $NOTGZIP fi zcat $1 | more # 使用过滤命令'more.' # 当然, 如果你愿意, 也可以使用'less'. exit $? # 脚本将把管道的退出状态作为返回值. # 事实上, 也不一定非要加上"exit $?", 因为在任何情况下, # 脚本都会将最后一条命令的退出状态作为返回值.例子
这个例子的逻辑很简单,就是先判断命令行参数有没有,第一个是不是.gz文件,都是的话,打开即可。
其中有这么几个点需要学习一下:
1、设置清楚错误码,
2、$#是命令行参数个数
3、&2是stderr
4、[ ! -f “$filename” ] 不是一个一般文件,就是不存在这个文件.
5、${$string##*.} 这个是重点,是字符串操作(包括提取,删除,替换):
6、如何测试?我的方法是: find /usr -name "*.gz" -type f -exec bash 7-7.sh '{}' ';' #但是这种方法,可能会按q无法结束,因为有很多的.gz文件等着你去q,哈哈~按ctrl+C吧~