如何脱离 grep, sed, awk 完成一些批量任务?

2015-01-11 11:38:38 +08:00
 ysmood

如何脱离 grepsedawk 完成一些批量任务?

首先要提下 python 的 -c 选项,比如打印 10python -c 'print 10' 。即使不用 -c 选项,用 pipe 也是可以的,如 echo 'print 10' | python。这种用法非常标准,ruby,lua,node 之类的一般解释器都支持。ruby 甚至支持 -n-p 这种便利的选项。

一旦你理解了 pipe 的基本原理:

  1. 你可以用 cat | python,输入完 print 10enter, ctrl-D 结束输入。这样做的好处是可以随意打回车或者引号了。

  2. 如果你会 vim,可以开空 vim,然后 insert 模式下输入 print 10,然后命令模式下输入 :w !python 结束输入。用 vim 的好处是有语法高亮等高级功能而不产生临时文件。

Ruby

这里先讲 ruby, 因为 mac 自带 ruby,它原 生字符串处理库 很强大,而且有便利的撇号 "`" 和 "%x",感兴趣的话可以看看文档,即使你不懂 ruby 也会觉得这些让它非常适合处理 cli 任务。

为了便于输入,系统里加了几个 alias:

alias r_init="ruby -e 'require \"FileUtils\"; F, D = FileUtils, Dir'"
alias r="r_init -e"
alias rp="r_init -p -e"
  1. 打印第一个含有数字的文件名

    bash: ls -1 | grep -m 1 -e '.*[0-9].*'

    ruby: r 'puts `ls`[/.*\d.*/]' (少打 9 字符)

  2. 替换所有文件名中的 y 为 30 个 o

    bash: ls -1 | sed s/y/oooooooooooooooooooooooooooooo/

    ruby: ls -1 | rp '$_.gsub! /y/, "o" * 30' (少打打 12 字符)

    你还可以继续把这个命令 pipe 下去。

  3. 批量重命名文件为 “[3 字宽 0 补齐递增]” + “原文件名” 的形式

    bash: 这个 sed 我不好好查查文档也写不出 ;P

    ruby: r 'D["*"].each_with_index { |f, i| F.mv f, "[%03d]" % i + f }'

    操作类似如下命令:

    mv \"a\".txt \[001\]\"a\".txt
    mv a\ b.txt \[002\]a\ b.txt
    

    文件名中可能有引号或空格,使用 sed 拼接 mv 命令时需注意转义,而这里 ruby 调用的原生方法,所以无需转义即是安全的。

    不借助三方库,其他语言很难写的如此之短,且易于理解和记忆。

Node & Coffee

node 本身的库很基础,不足以完成日常所需,但是它的三方库往往是最容易使用和获取的。正是如此,用之前准备工作要更多

由于没有主流系统自带 node,你需要先安装它,然后配置系统环境,这个步骤必不可少。
执行 echo `npm config get prefix`/lib/node_modules,将获得的结果设置到环境变量 NODE_PATH 中,当然你不担心 npm 龟速的话,也可以直接把如下代码加入到你的 bashrc 之类的文件里:

export NODE_PATH=`npm config get prefix`/lib/node_modules

有了这个环境变量后我们才可以 require 全局安装的三方库。然后我们再添加个 shell 函数:

c() {
    coffee -e '
require "shelljs/global"
F = require "fs"
puts = (args...) -> console.log args
$1'
}

然后我们就可以安装一些三方库来测试下效果了,比如执行 npm i shelljs 安装好三方库,然后执行 c 'puts ls "*"',如果正常打印了,当前目录的文件就基本配置完毕了。

在 require 一堆三方库之后 node 的解决实际问题的能力会非常强大,V8 的爆发处理能力虽然吃内存,但很多下情况会比 ruby 或者 python 快很多,异步 IO 也会在处理慢移动磁盘时很便利。

这里只提一个常见的问题,关于 coffee 的单行代码怎么写的问题。这个问题上 coffe 和 python 最大的不同在于多了一个 then 关键字:

当然,由于 js 是最 buggy 的 duck typing 语言之一, 外加大部分的功能和库都是能用 chain 范式完成功能的,one line code 非常容易写。

如果你是个三方库控的前端人员,且不畏惧 js 的种种 buggy 问题,node 会是不二选择。

Perl & PHP

当前 2015 年初,perl + php 这方面的处理库实际上比 node 要多很多。而且 perl 或者 php 解释器大部分系统自带,生态环境非常无解的强大。
除了语法相对于后来的语言来说有些不简练,找不到什么不选它的理由。perl 我也不多写例子了,官方教程 hello world 之后都不是教你 for 循环之类的,直接就上文件 IO 操作,由此可见一斑。

如果如果习惯了用 shell,并且觉得在沙漠中寻找绿洲才是王道,perl 在等你。

Python

这些年的使用经验告诉我,它比较适合对 python 知根知底的人,初学者想用它玩弄文件系统会碰到各种问题,首先把多个命令写到一行就会有很多问题。再比如 python2 和 python3 的一些问题,文件名编码的问题等。

由于本人学识尚浅,这里就不在各位看官面前班门弄斧举 python 的例子了。

如果你是个爬行动物爱好者,并且觉得你之前学的 python 技能能无坚不摧,请选择 python。

Others

Go,Haskell 之类的静态类型编译语言都不在讨论范围内。

总结

当然上述方法对我来说可能有些过时了,现在处理一些难以复用的任务都是用 sublime 或 vim 可视化(非编程)完成。由于具体步骤太直观可视化,这里难以用文字描述,就不赘述了,有机会的话可以录视屏演示下。

常复用的也不会用命令行敲了,太浪费生命,直接写成库或者 snippet 存在 gist 之类的地方。此外需要高性能的时候也不会不停的在 shell 里 loop 调用类似 mv, cp 之类的程序,而是直接写 C 之类的调用内核方法。

写这么多不是想说你应该学会 ruby 什么的,每个工具都有它适用的场景:

我们往往最需要的可能是锻炼想象力,而不是评判什么工具最好,否则我们的记忆力永远不够用。

原文地址

5722 次点击
所在节点    程序员
32 条回复
Havee
2015-01-11 12:03:49 +08:00
这里记得是禁止原文转载的吧
ysmood
2015-01-11 12:05:04 +08:00
@ysmood 请注意看作者,都是我
jason52
2015-01-11 12:06:37 +08:00
我觉得这既不是 发现了新领域,也不是解决了新问题。 有点类似于 如何在不使用轮子的情况下让汽车可以行驶的问题。

虽然在一定的限制条件下,可以提出一种解决问题的方案,但是价值几许依旧值得考虑。你可以说我铺一个磁悬浮的轨道,让汽车可以 **实现** 在不使用轮子的情况下到达另一个目的地,但是灵活性,经济角度等权衡考虑,此解决方法未必就比使用轮子更好。

当然还可以从启发人的角度进行进一步探讨。这就是工程之外的问题了。

楼主也并未否认命令行工具的价值,并且给出了每个工具都有它适用的场景的结论,但是从锻炼想象力的角度进行论述,在这个场景下有点不太合理。
ysmood
2015-01-11 12:07:48 +08:00
@Havee 最近 Github 比较难打开,就全部贴过来了,同步发布的,不知道算不算违规。
ysmood
2015-01-11 12:10:40 +08:00
@jason52 思维可能有点跳跃了,大概想表达,利用好自己熟悉的东西,用创造力让它发挥更好的作用,往往最能解决当下所需。比如利用 pipe + python 解决 python 难以单行写代码的问题。
xcv58
2015-01-11 12:20:01 +08:00
grep, sed, awk 连 pipe 都不需要用。
另外它们或者替代品都有专门的优化。
不明白为啥要脱离它们。
aheadlead
2015-01-11 12:21:22 +08:00
新技能学习..
ysmood
2015-01-11 12:29:07 +08:00
@xcv58 是看了这篇的评论有感而发 http://v2ex.com/t/160899。我平时还是会大量使用 grep sed 的。只是提供一些可能的思考方式,抛砖引玉用。
ysmood
2015-01-11 12:33:35 +08:00
@xcv58 另外如果能不用 pipe 而写的比我上面的例子更简短易懂,求赐教,我也是想扩展下思路,先谢谢了~
xcv58
2015-01-11 12:34:58 +08:00
@ysmood 个人观点,这些都是工具,哪个适合当时的场景就用哪个。
很多时候已经有了 best practice 的时候真的没必要费时间用其他工具再来做一遍。
因为理论上都是基于图灵机,但是实践上就天差地别了。
acoada
2015-01-11 12:36:54 +08:00
grep太好用了,sed和awk写些简短的逻辑也好用,复杂的就perl处理,它们之间有冲突吗?
曾经尝试过awk script,实在是。。
ysmood
2015-01-11 12:45:29 +08:00
@xcv58 我也是昨天刚看了电影《The Imitation Game》,我这儿也不是想跟任何最佳实践较真。我文中最后也表达了类似观点,只是我觉得我们可以在使用 best practice 的时候不要忘记自己的思考,以及思考的乐趣。我写这文纯是想娱乐下大家,若是觉得有趣笑一笑,我就非常感激了,即使觉得我愚钝,我也没什么想反驳的,毕竟我也是刚入门~
xcv58
2015-01-11 12:58:52 +08:00
@xcv58
第一个:
ag -m 1 -g '.*[0-9].*'

第二个:
ls -1 | sed s/y/"$(seq -f "0" -s '' 30)"/
把你的 Alias 转义过来应该比这个长吧。

第三个应该用 awk 和 xargs 来做就行了啊,懒得写了。
xcv58
2015-01-11 13:03:02 +08:00
@ysmood 我不是反对思考,我认为不应该放到这种一次性的工作上。
xcv58
2015-01-11 13:08:10 +08:00
@ysmood 当然你如果觉得这样很锻炼想象力的话,我也没啥好反对的。
ysmood
2015-01-11 13:49:01 +08:00
@xcv58 ag 是啥?你也可以用 alias 啊,这个不反应逻辑的长短性啊,没必要在意这个。入口点命令长度忽略,只看作用部分。第二个你这么写明显复杂很多吧?ag 应该就是某种 grep 的 alias 吧?

我第一个作用部分只有 `ls`[/.*\d.*/],第二个只有 $_.gsub! /y/, "o" * 30
9hills
2015-01-11 13:49:28 +08:00
Python one-line 坑比较多,而且容易出问题。不过谨慎点还是可以搞的

比如我最常用的是用Python在命令行解析JSON格式,感觉略烦。。。但是还好吧

curl http://xxxxx/api/v1/xxx.json | python -c "import sys,json; r=json.loads(sys.stdin.read()); print '\n'.join([i for i in r['data'] if i.startswith('aaa')]) + '\n'"

awk/sed 处理JSON 显然是力不从心,jq这样的外部工具也不太适合(毕竟不是标准工具)
9hills
2015-01-11 13:52:59 +08:00
Python用户可以试试 "https://github.com/Russell91/pythonpy"

py 'expression' ≅ python -c 'print(expression)'
ysmood
2015-01-11 14:10:15 +08:00
@9hills 我平时都是写个 shell 函数来做,比如 ping 一个域名用下面这个函数就可以直接 ping 任意一个网址,而不是只能 ping 标准 host:

ys-ping http://test.com/other/path?with=query

ys-ping test.com

ys-ping () {
ret=$(python -c "
import urlparse
s = '$1'
if s.find('://') < 0:
s = 'http://' + s
host = urlparse.urlparse(s).netloc
print(host)
")
ping $ret
}

一行写 python 太伤神,还是换个方式来比较好。

话说我很好奇,怎么没有 ruby 党站出来吐个槽什么的。
xcv58
2015-01-11 14:23:59 +08:00
@ysmood http://geoff.greer.fm/ag/ 搜索一下 不好乱猜

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/161060

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX