程序应该只关注一个目标,并尽可能把它做好。让程序能够互相协同工作。应该让程序处理文本数据流,因为这是一个通用的接口 –Malcolm Douglas McIlroy(Unix 管道发明者)

此前写过这篇:正则表达式一例

我试着用多种语言去完成这个任务,当我试着用 bash 和 perl 去解决这个问题的时候,发现 perl 和命令行的的结合十分紧密,很容易将 perl 作为管道的一截,就像 sed 或是 grep 一样。

而管道是我极喜欢的 Unix 特性之一。

一旦习惯了管道,将程序作为过滤器,就十分容易与 Unix 工具箱中的其他组件协作,如此一来绝大多数的工作都省了!过滤器之间协作的力量常常十分惊人。

##Pipeline 管道(Pipeline)依赖于这样的约定,每个程序一开始有两个 I/O 数据流可用:标准输入和标准输出,许多程序都可以写作过滤器,从标准输入顺序读入数据,并且向标准输出写数据。

###语法 典型语法是使用 ASCII 中的垂直线|

###demo ls -l | less

ls 用于在 Unix 下列出目录内容,less 是一个有搜索功能的交互式的文本分页器

###从管道中获益 上边的 less 将程序员从为自己的软件开发分页器的负担中解放了出来:他们只需要把他们的输出用过“管道”导入到 less 程序中即可,可以完全不顾分页问题

这样就避免了代码的臃肿,降低和全局复杂度

《代码大全》中给出过一个观点:

软件工程的本质即是管理复杂度

管道触进了“Do one thing, Do it well”,于是当然也有利于“Keep it Simple Stupid”

而这些对于一个复杂项目的成败都是至关重要的

###other demo tr -c '[:alnux]' '[\n*]' | sort -iu |grep -v '^[0-9]*$'

以上操作把标准输入的文本生成了经过排序的单词表送到标准输出

  • 第一个命令把标准输入中非字母和数字的字符在标准输出中转化为新行。
  • 第二个命令对标准输入的行进行排序,对于重复的相邻行只保留一个,然后把排好序的数据写到标准输出。
  • 第三个命令去掉所有只包含数字的行。

##Pipeline and Python

那么 Python 程序能否方便地作为过滤器放到管道里呢。毕竟我不想捏着鼻子写 perl。

github 了一下,找到这个pythonpy

下边我们试着用 pythonpy 来完成正则表达式一例中的任务

环境

  • zsh
  • virtualenv(python3) ps:涉及到中文的问题尽量用 python3
  • pip install pythonpy

代码

1
2
3
4
5
filenames=(ls /PATH/*.txt)
for file in $filenames
do
cat $file | py -x "re.sub(r'[\u4e00-\u9fa5].*','',x)">output_$file
done

怎么样,够简洁吧!

pythonpy demo

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
#计算
py '3 * 1.5' #4.5

#py -x :treat each row of stdin as x
py 'range(3)' | py -x 'int(x)*7' #0\n7\n14
echo 'a1,b1,c1\na2,b2,c2' | py -x 'x.split(",")[1]' #b1\nb2
##Append ".txt" to each line of input
py 'range(3)' | py -x 'x + ".txt"' #0.txt\n1.txt\n2.txt
##Append ".txt" to every file in the directory,ps:还是使用ipython或是shell的for来做吧
ls | py -x '"mv `%s` `%s.txt`" % (x,x)' | sh

#py -l :treat list of stdin as l
py 'range(3)' | py -l 'sum(int(x) for x in l)'

#Count words beginning with each letter 。多熟悉collections(python cookbook),集合的特性相关
cat /usr/share/dict/words | py -x 'x[0].lower()' | py -l 'collections.Counter(l).most_common(5)'

python 脚本作为过滤器

如果我们想将 python 脚本当做过滤器用在管道里,关键便是让它能处理输入(stdin)输出(stdout)流

1
2
3
4
#myscript.py
import sys
for line in sys.stdin:
  sys.stdout.write(line) # sys.stdout.write与print的区别是,print会默认添加换行符

使用:cat text.txt|python myscript.py

#参考