shell私房菜part1传送门
shell私房菜part3 传送门
命令行是键盘屏幕出来以后,图形界面风靡之前,主流的人机交的渠道方式,现下用惯图形界面的同学估计要吐槽它的单调素颜。
但是它还是具有强大的生命力,因为它存在的时间够长,被n多的大牛把玩过打磨过,它是精巧高效的工具,当然学习曲线显得陡峭。我是金庸迷,在我看来,这东西有点“重剑无锋,大巧不工”的范儿。
我们来瞻仰一下Unix系统上管道机制的发明者,也是Unix文化的缔造者之一的Douglas McIlroy大牛的哲学
1. 程序应该只关注一个目标,并尽可能把它做好。(I love KISS,keep it simple stupid)
2. 让程序能够互相协同工作。(都来用俺发明的管道吧)
3. 应该让程序处理文本数据流,因为这是一个通用的接口。(管道要高效,那就让它内里流动最简单的东西吧)
如果我们回顾上一个part最后写成的shell命令行
cat abcd.csv |grep -v location|sed -e 's:"::g'|sed -e 's:\s::g'|awk -F',' '{print "update tkm_cinema set latitude='\''"$2"'\'',longitude=\047"$3"\047 where id="$1";"}' > test.sql
分开来看,cat,grep,sed,|管道,>输出重定位符,它们都是符合第一条的,KISS。awk是个逆天的东西。
它们的输入输出都是文本数据流,符合第三条。
使用管道串接,协同完成了一个任务,这是第二条。
Ok,复习完毕,开始今天的新内容:循环,条件,变量。和昨天一样
红色字是命令行啊,(在括号里的绿色的字是注释啊),浅蓝色字是屏幕输出啊。
还是上实战案例
Case:我手里有一份IOS设备token的list文件,每行一个,数量有17k+,需要切割成1000行一个小文件,并且以特定的文件后缀名“custom.txt”结尾。
像切割文件这种事,一定是被解决过有工具的,可能还很多,我恰好知道一个叫split,但是参数记不得了,于是先man一下,man是shell的帮助文档工具哦,常用的命令工具都能找到,比度娘快哦,当然基本都是鸟文写的。
man split(man是手册单词的简写哦,女程序员如果觉得不爽要用woman我也能教个方法给你,^_^, man后面跟你想要了解的命令名就好,进入man的界面,q是退出,gg到文档头部,G到文档尾部,/xxxx可以搜索xxxx字符串(搜索后,n跳到下一个命中xxxx的地方),世上本没有shell熟手,man看的多也便成了熟手,split简单的,不到两屏幕的鸟文,很快就后就看到需要用到的参数了,用-l参数,定义按多少行分割文件,用PREFIX可以自定前缀,)
split [OPTION] [INPUT [PREFIX]]
-l, --lines=NUMBER
put NUMBER lines per output file
搞清楚split用法就来分割文件吧。
split -l 1000 token.list pushList(对照split [OPTION] [INPUT [PREFIX]]看,OPTION那里是-l 1000,表示按每1000行一个文件;INPUT那里是token.list,表示待分割输入文件;PREFIX是pushList,表示希望的分割出来的文件的前缀)
看看分割出来的结果,
ls -l(ls,显示文件列表命令,常用到令人发指的程度,有n多的参数可选,强烈建议大家man一下,重点关注下-r –S –t -h)
-rw-r--r-- 1 mengchang users 184664 Nov 14 09:43 token.list
-rw-r--r-- 1 mengchang users 10587 Nov 15 11:08 pushListaa
-rw-r--r-- 1 mengchang users 10845 Nov 15 11:08 pushListab
。。。。。。
-rw-r--r-- 1 mengchang users 3035 Nov 15 11:08 pushListar
看看每个文件的行数是否是期望的
wc -l *(wc是数文件行的工具,绝对的简单傻,但是好用,man它一下吧)
1000 pushListaa
1000 pushListab
1000 pushListac
1000 pushListad
。。。。。。
282 pushListar
17282 token.list
34564 total
So far so good,看上去就剩下最后一步了,给分割出来的各个文件加上后缀。然后就到今天的主角出场了,铺垫很累人的,有木有。
按照我的习惯,首先把循环的架子写好如下
for i in `ls push*`;do echo $i;done;(作为注释,俺觉得这行的信息量很大。注释兄,闲话少说,阿里人很忙的好吧。
首先,三个分号分隔出来的三部分,被shell认为是三个完整语句,你其实可以输入到for i in `ls push*`;然后回车,你会发现屏幕上给出的是一个继续输入的提示如下,
for i in `ls push*`;
>
然后你可以输入第二段do echo $i;然后回车,还是继续输入的提示
for i in `ls push*`;
> do echo $i;
>
最后你输入第三段done;回车后就得到输出了。这和写在一行的效果一样。
然后我们来看第一段for i in `ls push*`;特意放大一点,for循环命令,i循环变量,这里其实相当于赋值,或者说左值,不用带$符号,in表示后面要跟枚举值了。再往后是一对引号,但是这里的引号不是单引号,而是反引号,在键盘的数字1的左边那个(或是esc键下面那个)。反引号范围里是一个shell命令,它将先被执行,然后替换反引号所在的位置。
ls push*将会列出当前目录下,所有以push开头的文件,*是通配符啊,这也是一种shell的基础设置,表示匹配0个或是多个任意字符,如此就把输入文件token.list给滤掉了。有等同效果的实现,比如用part1讲到的grep,这样写ls|grep push也很ok的
所以实际上for执行时的命令行已经是for i in pushListaa pushListac pushListae pushListag pushListai pushListak pushListam pushListao pushListaq pushListab pushListad pushListaf pushListah pushListaj pushListal pushListan pushListap pushListar;,反引号替换威武吧。
然后看第二段do echo $i;do是循环体开始的标示,echo,是打印命令,一贯的又傻又天真,非常符合阿里价值观,$i是对循环变量的使用,或者说是右值,这就需要带上$符号了。
最后一段done;和do对应,表示了循环体的结束。
)
pushListaa
pushListab
pushListac
pushListad
。。。。。。
先写循环的架子我觉得是个好习惯,首先是因为写在一行的for循环其实蛮容易写错的,经常会少写了一个分号,少写了一个do,先把架子写好,把循环的变量打印出来,那之后对循环变量的操作就能确定是靠谱的了。
for i in `ls push*`; do echo $i; mv $i $i.custom.txt;done;ls -l;(还是用上箭头翻出来,在done语句的前面写一个mv命令,mv $i $i.custom.txt;两个出现$i将会被替换成循环变量。循环完成,done之后用ls命令把当前目录的文件打出来看看)
-rw-r--r-- 1 mengchang users 10587 Nov 15 11:08 pushListaa.custom.txt
-rw-r--r-- 1 mengchang users 10845 Nov 15 11:08 pushListab.custom.txt
-rw-r--r-- 1 mengchang users 10609 Nov 15 11:08 pushListac.custom.txt
。。。。。。
-rw-r--r-- 1 mengchang users 3035 Nov 15 11:08 pushListar.custom.txt
-rw-r--r-- 1 mengchang users 184664 Nov 14 09:43 token.list
分割文件,重命名的case就打完收工了。
Shell中的for还有一个我极为常用的范式,就是配合seq命令执行次数确定的循环操作。超有用的,给大家show一下。
很傻很天真的seq怎么用,let’s man。
man seq
SEQ(1) User Commands SEQ(1)
NAME
seq - print a sequence of numbers
SYNOPSIS
seq [OPTION]... LAST
seq [OPTION]... FIRST LAST
seq [OPTION]... FIRST INCREMENT LAST
seq就是打印一个数字序列的命令啊,天生配合for使用。那么当一个case是把某个操作重复n遍,you know how to do
for i in `seq 5 10`;do echo $i;date;echo "do something";sleep 2;done;(seq产生5到10的数字序列,由于放置于反引号中,将先于循环for执行,生成5 6 7 8 9 10作为for循环的枚举循环取值,
date是打印时间的函数,很有用,很多参数,值得一man,
sleep是睡神,大家膜拜一下,单位是秒,
所以上面这个循环就是要循环5次,在每次循环里面,打印循环变量,打印当前时间,睡两秒)
5
Thu Nov 15 13:55:01 CST 2012
do something
6
Thu Nov 15 13:55:03 CST 2012
do something
7
Thu Nov 15 13:55:05 CST 2012
do something
8
Thu Nov 15 13:55:07 CST 2012
do something
9
Thu Nov 15 13:55:09 CST 2012
do something
10
Thu Nov 15 13:55:11 CST 2012
do something
for配合反引号的用法只是我最常用的,反引号可以用$()等价替换,for i in `ls`等价于for i in $(ls)。这里又会扯出关于括号的一堆事,我给个文档看看吧
关于shell的for循环的http://www.linuxdiyf.com/viewarticle.php?id=206569
关于shell各种括号的http://xuke1668.blog.51cto.com/2129485/853107
今天本来还要写if条件判断的,写不动了,好多事。下回继续。