入门bash Shell脚本
在进行批量的操作,或者繁琐的操作时,可以使用shell编写脚本来简化操作。我使用过众多别人编写的脚本,但是自己从来没有写过。今天正好碰到一个问题,不想繁琐的去挨个操作,那么就尝试着写一个脚本来方便操作一下吧。首先说一下自己遇到的问题,今年去参加了CocoaChina开发者大会,会上的PPT很好,今天在网上把他们下载了下来,但是问题是,他们的命名很长而且前一部分都是一样的,类似于”CocoaChina2013开发者大会-……”,前一部分都是重的,在Finder里看起来很是不方便,根本看不到有用的文件名信息,所以我决定要把这十多个文件重命名去掉前缀。
从来没有编写过shell脚本,向来都是使用shell命令的我,先从学习编写shell脚本文件开始吧!
在文本编辑器中第一行(必须是第一行)首先键入#!/bin/sh,符号#!用来告诉系统它后面的参数是用来执行该文件的程序。
注释
在shell中#代表注释,直到这一行结束。
变量
在shell中,变量都由字符串组成,变量名无需提前声明,写了就可以直接赋值,变量名=值(这里等号两边一定不能有空格)。取变量时要使用$符号。有时在一长串字符串中包含变量,可以对变量名加上{}来区分。例如有一个变量num=2echo "this is the $numnd",这样会有问题,我们要写成echo "this is the ${num}nd"。
流程控制
只写一下本次用到的for循环,其他的流程控制以后用到时在学习。for-loop表达式查看一个字符串列表 (字符串用空格分隔) 然后将其赋给一个变量:for var in ….; do ….done在下面的例子中,将分别打印ABC到屏幕上:
#!/bin/sh
for var in A B C ; do
echo "var is $var"
done
这里参考的文章是Linux shell脚本编写基础
在我写的shell脚本中则是使用循环输出当前文件夹下文件的名字:
#!/bin/sh
for aFile in *; do
tmpFile=`basename $aFile`
echo 原文件名:${tmpFile}
done
接下来就是对获取到的每一个文件名的字符串进行截取,删掉不需要的部分:
#!/bin/sh
for aFile in *; do
tmpFile=`basename $aFile`
newName=${tmpFile#C*-}
echo 原文件名:${tmpFile}
echo 新文件名:${newName}
done
这里学习了一下shell字符串的操作知识,:选取子串 #正向截取子串 %逆向截取子串 ##正向最长匹配 %%逆向最长匹配。
str="abcdef"
expr substr "$str" 1 3# 从第一个位置开始取3个字符, abc
expr substr "$str" 2 5# 从第二个位置开始取5个字符, bcdef
expr substr "$str" 4 5# 从第四个位置开始取5个字符, def
echo ${str:2} # 从第二个位置开始提取字符串, bcdef
echo ${str:2:3} # 从第二个位置开始提取3个字符, bcd
echo ${str:(-2)} # 从倒数第二个位置向左提取字符串, abcde
echo ${str:(-2):3} # 从倒数第二个位置向左提取3个字符, cde
str="abbc,def,ghi,abcjkl"
echo ${str#a*c} # ,def,ghi,abcjkl一个井号(#) 表示从左边截取掉最短的匹配 (这里把abbc字串去掉)
echo ${str##a*c} # jkl, 两个井号(##) 表示从左边截取掉最长的匹配 (这里把abbc,def,ghi,abc字串去掉)
echo ${str#"a*c"} # 空,因为str中没有子串"a*c"
echo $[str##"a*c"} # 空,同理
echo ${str#d*f) # abbc,def,ghi,abcjkl,
echo ${str#*d*f} # ,ghi,abcjkl
echo ${str%a*l} # abbc,def,ghi一个百分号(%)表示从右边截取最短的匹配
echo ${str%%b*l} # a 两个百分号表示(%%)表示从右边截取最长的匹配
echo ${str%a*c} # abbc,def,ghi,abcjkl
这里参考的文章Bash Shell字符串操作小结
最后就是将文件重命名了:
#!/bin/sh
for aFile in *; do
tmpFile=`basename $aFile`
newName=${tmpFile#C*-}
echo 原文件名:${tmpFile}
echo 新文件名:${newName}
mv $tmpFile $newName
done
运行时发现只有部分文件被成功重命名了,其余的都不成功,而这些不成功的文件名中都包含空格,空格是很大一个问题,在获取原文件名时文件名就被空格截断了,导致文件名不全。因而重命名也是失败的,找不到源文件。参考这篇文章SHELL技巧:处理文件名中的那些空格找到了解决方法,对变量添加""使空格被正确处理,这不是最好的方法,但是在我这个小小的脚本中完全可以了。
文章中还介绍了一种终极解决方法就是设置IFS(the Internal Field Separator),但是在设置之前先保存当前的IFS,操作完成之后在设置回去。
#!/bin/sh
SAVEIFS=$IFS
IFS=$(echo -en "\n\b")
#进行操作
IFS=$SAVEIFS
但是我这样使用之后经过测试发现,并不能解决问题,文件名被读取成其他怪异的形式了,空格是被正常读取了但是-也被读取成空格了,我不知道这是为什么,有知道的欢迎告诉我。
最后附上自己写的完整的shell脚本,虽然很短,但这是第一次写,也算是个入门吧。
#!/bin/sh
#===============将文件名的前缀部分去掉=============
#====处理文件名中带空格的问题,先保存$IFS变量,经测试这么做还是会有问题部分字符会丢失
#SAVEIFS=$IFS
#IFS=$(echo -en "\n\b")
for aFile in *; do
#对变量加上双引号会避免文件名中有空格的问题
tmpFile=`basename "$aFile"`
#截取文件名字符串中的前一部分
newName=${tmpFile#C*-}
echo 原文件名:${tmpFile}
echo 新文件名:${newName}
#对文件进行重命名
mv "$tmpFile" "$newName"
done
#=====将$IFS恢复为原来的状态
#IFS=$SAVEIFS
总结一下需要注意的地方
[*] 在写shell脚本时不能延续写其他代码乱加空格的习惯,空格在shell中很重要,随便加空格会导致shell脚本执行失败。
[*] 还是空格问题,使用basename命令时,获取到的文件名如果有空格的话将不会获取之后部分,可以对变量添加""解决问题。
[*] cpmv等命令要求文件命中同样不能有空格,也可以在脚本中对变量添加""解决。
[*] 刚开始写shell脚本时最好写一句测试一句,要严谨不能想当然。
页:
[1]