shell私房菜part2

shell私房菜part1传送门

shell私房菜part3 传送门

命令行是键盘屏幕出来以后,图形界面风靡之前,主流的人机交的渠道方式,现下用惯图形界面的同学估计要吐槽它的单调素颜。

 

但是它还是具有强大的生命力,因为它存在的时间够长,被n多的大牛把玩过打磨过,它是精巧高效的工具,当然学习曲线显得陡峭。我是金庸迷,在我看来,这东西有点“重剑无锋,大巧不工”的范儿。

 

我们来瞻仰一下Unix系统上管道机制的发明者,也是Unix文化的缔造者之一的Douglas McIlroy大牛的哲学

1.       程序应该只关注一个目标,并尽可能把它做好。I love KISSkeep 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设备tokenlist文件,每行一个,数量有17k+,需要切割成1000行一个小文件,并且以特定的文件后缀名“custom.txt”结尾。

 

像切割文件这种事,一定是被解决过有工具的,可能还很多,我恰好知道一个叫split,但是参数记不得了,于是先man一下,man是shell的帮助文档工具哦,常用的命令工具都能找到,比度娘快哦,当然基本都是鸟文写的。

man splitman是手册单词的简写哦,女程序员如果觉得不爽要用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,表示待分割输入文件;PREFIXpushList,表示希望的分割出来的文件的前缀)

 

看看分割出来的结果,

ls -lls,显示文件列表命令,常用到令人发指的程度,有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产生510的数字序列,由于放置于反引号中,将先于循环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条件判断的,写不动了,好多事。下回继续。

你可能感兴趣的:(shell私房菜part2)