背景
在工作中,难免会使用shell处理一些繁杂的事情,比如图片的筛选分类等,但是在书写脚本时一不小心就会出问题,下面,记录一下容易出问题的地方。
内容
1 路径带空格
比如说一个文件夹里面都是图片,你要把图片删除掉,不知道是谁脑残的在命名图片的时候加上了空格,比如我们其中一张图片的名字叫小花的 照片.PNG
,而你的代码是这样的,
#!/bin/bash
for p in ./*;do
rm $p
done
那么你在写如下代码遍历文件夹时,一定会遇到下面的错误
rm: ./小花的: No such file or directory
rm: 照片.PNG: No such file or directory
怎么会出现这样的错误呢,我想你一定猜到了,对,是空格
,空格把这个图片的名字分成了两部分,shell在读取字符串的时候,会以这样的形式去读取:
rm 小花的 照片.PNG
其实相当于rm 小花的
和rm 照片.PNG
,那怎么办呢,我们必须让shell 指令读取整个字符串,而不是把后面的参数认为是多个文件名,最简单的方法是用双引号
把我们的变量引起来。这里我们延伸一下,在shell字符串中,有两种方式表示字符串:
1 单引号引起来:
a、 单引号的任何字符都会原样输出,单引号里面的变量无效
b 、单引号里面不能出现单个单引号(必须成对出现)
2 双引号引起来
a 、双引号里面可以有变量
b、双引号里面可以出现转义符
通过上面的解释,我们知道了,首先$p
是变量,如果你用单引号括起来,那么我们的$p
就会变成了$p
字符串,已经再在是代表着小花的 照片.PNG
的变量,用双引号
就不会出现问题。至于说更复杂的做法这里不推荐,毕竟脚本就是为了简化。那么更改后的脚本时这样的:
#!/bin/bash
for p in ./*;do
rm "$p"
done
2 比较浮点数的大小
在工作种虽然遇到的不多,但是遇到了才发现自己还是有很多东西需要掌握的啊。比如,在工作中要从一个日志文件中筛选出得分在0.9以上的图片,那么我们就要比较得分和0.9的关系了,于是我自信的写下了以下代码:
#!/bin/bash
6 logFile="/Users/lilei/Desktop/logtxt"
7 while read line;do
8 score=$($line##*==)
9 if [ $score >0.9 ];then
10 echo $line
11 fi
12 done<$logFile
但是,却报这样的错误……
calculate.sh: line 9: 0.9: No such file or directory
也就是说if
判断这句除了问题,
那么,我们如何进行浮点数判断呢,查资料后发现原来虽然bash
内置了对四则运算的支持,但是并不支持浮点运算,mygod,强大的bash
竟然不支持浮点数运算,那么我该怎么办???
经过一番google发现大概有两种方法:
2.1 利用bc
神器
bc
其实是linux
下计算语言,其完整的名称是An arbitrary precision calculator language
,能够帮助我们进行各种精度的数据运算,当然对于浮点数的计算也不在话下,其正确使用方式如下:
#!/bin/bash
6 logFile="/Users/lilei/Desktop/logtxt"
7 while read line;do
8 score=$($line##*==)
9 if [ `expr $score >0.9|bc` =1 ];then
10 echo $line
11 fi
12 done<$logFile
我们可以很清楚的看到上述表达其实是对括号内的表达式进行了一个运算,然后判断是否为真。
2.2 利用awk
对于awk的用法,自己可以百度脑补,下面我们来看看如何用它来判断浮点数的大小
#!/bin/bash
logFile="/Users/lilei/Desktop/logtxt"
while read line;do
score=$($line##*==)
result=$(echo | awk -v x=$score '{if(x>0.9){print 1}else{print 0}}')
if [ $result -eq 1 ];then
echo $line
fi
done<$logFile
当然上述awk的写法不止一种,以上只是其中一种……
3 坑爹的if
判断
如果你第一次接触shell,你肯定会对if
要和[
或[[
之间留空格,[
或[[
要和表达式之间留空格而想摔杯子,我也是,真觉得这种设计实在太变态,它会让我们经常因忘记加空格而出错,还有,如果你接触过shell,那么你知道[]
和[[]]
的区别吗?那么,我们接下来就来说说。
如果我们要判断一个路径是不是一个真实存在的路径,那么我们用[]
会这样写:
if [ -d "$dir" ];then
echo $dir
fi
为什么要加上双引号呢,前面第一条说过,那么要是用[[]]
怎么写呢,看如下:
if [[ -d $dir ]];then
echo $dir
fi
看到了吗," "
去掉了。在stackoverflow上给出的答案是[[ ]]
语法是对[ ]
语法的改进,前者有许多后者不具有的特性,如上面所展示的,[[ ]]
能够自动处理带空格的字符串,不用你去添加告诉它在什么范围内是字符串,当然,如果你要是硬要加双引号,那么也可以。
另外,[[ ]]
可以让你像c语言一样进行&&
、 ||
等多条件判断,还可以用<
、 >
进行字符串或数字比较,例如:
if [[ $a > 3 || $a < 10 ]];then
echo $a
fi
但是[ ]
却不行。
另外,[[ ]]
还提供了一种模式匹配的写法,通用语法if [[ $a = *patternStr* ]]
,例子如下:
path="/Users/guoran/Desktop/"
if [[ $path = *guoran* ]];then
echo $path
fi
这里需要说明一点,上面等号后面的匹配字符不能加双引号
,如果加了就会报语法错误(这个暂时还不知道是为什么?希望大神指教),等号可以写成==
或=
都可以,这基本是我最近写脚本时遇到的坑,记录一下。
思考
发现总是赖得记东西,总是以浪费时间,工作忙为借口来宽慰自己,但是发现不记东西,反而看过或写过的东西过了一段时间就又忘记了,从现在起,要强迫自己开始记录一下自己工作过程中遇到的问题,成长要从点滴做起,记住哈,少年!!!
参考资料:
https://stackoverflow.com/questions/3427872/whats-the-difference-between-and-in-bash
https://unix.stackexchange.com/questions/306111/what-is-the-difference-between-the-bash-operators-vs-vs-vs/306115