grep
,sed
,awk
完成一些批量任务?首先要提下 python 的 -c
选项,比如打印 10
:python -c 'print 10'
。即使不用 -c 选项,用 pipe 也是可以的,如 echo 'print 10' | python
。这种用法非常标准,ruby,lua,node 之类的一般解释器都支持。ruby 甚至支持 -n
和 -p
这种便利的选项。
一旦你理解了 pipe 的基本原理:
你可以用 cat | python
,输入完 print 10
后 enter, ctrl-D
结束输入。这样做的好处是可以随意打回车或者引号了。
如果你会 vim,可以开空 vim,然后 insert 模式下输入 print 10
,然后命令模式下输入 :w !python
结束输入。用 vim 的好处是有语法高亮等高级功能而不产生临时文件。
这里先讲 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"
打印第一个含有数字的文件名
bash: ls -1 | grep -m 1 -e '.*[0-9].*'
ruby: r 'puts `ls`[/.*\d.*/]'
(少打 9 字符)
替换所有文件名中的 y 为 30 个 o
bash: ls -1 | sed s/y/oooooooooooooooooooooooooooooo/
ruby: ls -1 | rp '$_.gsub! /y/, "o" * 30'
(少打打 12 字符)
你还可以继续把这个命令 pipe 下去。
批量重命名文件为 “[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 本身的库很基础,不足以完成日常所需,但是它的三方库往往是最容易使用和获取的。正是如此,用之前准备工作要更多。
由于没有主流系统自带 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
关键字:
c 'for i in [1..10] then puts i'
c
try F.readFileSync 'a.txt'; catch err then puts err'`当然,由于 js 是最 buggy 的 duck typing 语言之一, 外加大部分的功能和库都是能用 chain 范式完成功能的,one line code 非常容易写。
如果你是个三方库控的前端人员,且不畏惧 js 的种种 buggy 问题,node 会是不二选择。
当前 2015 年初,perl + php 这方面的处理库实际上比 node 要多很多。而且 perl 或者 php 解释器大部分系统自带,生态环境非常无解的强大。
除了语法相对于后来的语言来说有些不简练,找不到什么不选它的理由。perl 我也不多写例子了,官方教程 hello world 之后都不是教你 for 循环之类的,直接就上文件 IO 操作,由此可见一斑。
如果如果习惯了用 shell,并且觉得在沙漠中寻找绿洲才是王道,perl 在等你。
这些年的使用经验告诉我,它比较适合对 python 知根知底的人,初学者想用它玩弄文件系统会碰到各种问题,首先把多个命令写到一行就会有很多问题。再比如 python2 和 python3 的一些问题,文件名编码的问题等。
由于本人学识尚浅,这里就不在各位看官面前班门弄斧举 python 的例子了。
如果你是个爬行动物爱好者,并且觉得你之前学的 python 技能能无坚不摧,请选择 python。
Go,Haskell 之类的静态类型编译语言都不在讨论范围内。
当然上述方法对我来说可能有些过时了,现在处理一些难以复用的任务都是用 sublime 或 vim 可视化(非编程)完成。由于具体步骤太直观可视化,这里难以用文字描述,就不赘述了,有机会的话可以录视屏演示下。
常复用的也不会用命令行敲了,太浪费生命,直接写成库或者 snippet 存在 gist 之类的地方。此外需要高性能的时候也不会不停的在 shell 里 loop 调用类似 mv, cp 之类的程序,而是直接写 C 之类的调用内核方法。
写这么多不是想说你应该学会 ruby 什么的,每个工具都有它适用的场景:
我们往往最需要的可能是锻炼想象力,而不是评判什么工具最好,否则我们的记忆力永远不够用。
1
Havee 2015-01-11 12:03:49 +08:00
这里记得是禁止原文转载的吧
|
3
jason52 2015-01-11 12:06:37 +08:00
我觉得这既不是 发现了新领域,也不是解决了新问题。 有点类似于 如何在不使用轮子的情况下让汽车可以行驶的问题。
虽然在一定的限制条件下,可以提出一种解决问题的方案,但是价值几许依旧值得考虑。你可以说我铺一个磁悬浮的轨道,让汽车可以 **实现** 在不使用轮子的情况下到达另一个目的地,但是灵活性,经济角度等权衡考虑,此解决方法未必就比使用轮子更好。 当然还可以从启发人的角度进行进一步探讨。这就是工程之外的问题了。 楼主也并未否认命令行工具的价值,并且给出了每个工具都有它适用的场景的结论,但是从锻炼想象力的角度进行论述,在这个场景下有点不太合理。 |
5
ysmood OP @jason52 思维可能有点跳跃了,大概想表达,利用好自己熟悉的东西,用创造力让它发挥更好的作用,往往最能解决当下所需。比如利用 pipe + python 解决 python 难以单行写代码的问题。
|
6
xcv58 2015-01-11 12:20:01 +08:00
grep, sed, awk 连 pipe 都不需要用。
另外它们或者替代品都有专门的优化。 不明白为啥要脱离它们。 |
7
aheadlead 2015-01-11 12:21:22 +08:00
新技能学习..
|
8
ysmood OP @xcv58 是看了这篇的评论有感而发 http://v2ex.com/t/160899。我平时还是会大量使用 grep sed 的。只是提供一些可能的思考方式,抛砖引玉用。
|
10
xcv58 2015-01-11 12:34:58 +08:00
@ysmood 个人观点,这些都是工具,哪个适合当时的场景就用哪个。
很多时候已经有了 best practice 的时候真的没必要费时间用其他工具再来做一遍。 因为理论上都是基于图灵机,但是实践上就天差地别了。 |
11
acoada 2015-01-11 12:36:54 +08:00
grep太好用了,sed和awk写些简短的逻辑也好用,复杂的就perl处理,它们之间有冲突吗?
曾经尝试过awk script,实在是。。 |
12
ysmood OP @xcv58 我也是昨天刚看了电影《The Imitation Game》,我这儿也不是想跟任何最佳实践较真。我文中最后也表达了类似观点,只是我觉得我们可以在使用 best practice 的时候不要忘记自己的思考,以及思考的乐趣。我写这文纯是想娱乐下大家,若是觉得有趣笑一笑,我就非常感激了,即使觉得我愚钝,我也没什么想反驳的,毕竟我也是刚入门~
|
13
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 来做就行了啊,懒得写了。 |
16
ysmood OP @xcv58 ag 是啥?你也可以用 alias 啊,这个不反应逻辑的长短性啊,没必要在意这个。入口点命令长度忽略,只看作用部分。第二个你这么写明显复杂很多吧?ag 应该就是某种 grep 的 alias 吧?
我第一个作用部分只有 `ls`[/.*\d.*/],第二个只有 $_.gsub! /y/, "o" * 30 |
17
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这样的外部工具也不太适合(毕竟不是标准工具) |
18
9hills 2015-01-11 13:52:59 +08:00
|
19
ysmood OP @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 党站出来吐个槽什么的。 |
20
xcv58 2015-01-11 14:23:59 +08:00 via iPhone
@ysmood http://geoff.greer.fm/ag/ 搜索一下 不好乱猜
|
21
lsmgeb89 2015-01-11 14:29:48 +08:00
awk,sed,grep 要比 python 和 ruby 略快吧?
|
22
ysmood OP @lsmgeb89 通常情况下 C 处理速度肯定更快,我文章后面也提到了这个问题。不过你感兴趣的话可以看看这篇 “纯 js 写的 mysql parser,比 C 快” http://2012.jsconf.eu/speaker/2012/09/05/faster-than-c-parsing-node-js-streams-.html
我的理解是某种特定情况下脚本语言可能在字符串处理上能更容易优化解释器。而静态编译语言要编写同等优化程度的解释器,虽然是可能办到的,会难太多,以至于人们更愿意节省掉这部分时间去创造新的方案。仅仅个人观点,可能是错误的。 |
23
ysmood OP @xcv58 ag 还是上 github 页吧 https://github.com/ggreer/the_silver_searcher。看来可以用于替换 grep 了,自动 .gitignore 这个非常不错,长姿势了!
|
24
RemRain 2015-01-11 15:07:15 +08:00
其实我觉得直接用 grep awk sed 处理挺好,为啥要脱离呢,楼主给的几个例子,优点都是可以少打几个字符,但少打的这几个字符并没有省下多少力气,反而更费事。平时在打命令的时候,我特别怕各种标点符合,尤其下以下几种:
1. 引号。由于引号都是成对出现的,一旦使用了,就得用一对,另外必须思考用单引号还是双引号,是否有嵌套的情况,是否有字符需要转义。 2. 反撇号。输出可能有空格、换行之类的,使用了反撇号的情况下,有可能还得再用引号。 3. $、! 及转义符号。 4. 各种括号。 一旦有上述的标点符号各种嵌套,输入不顺手不说,可能得调试几次,命令才能正确执行。相比多一两个参数,命令长一点反而是好事。 说下楼主的两个例子: 1. 打印第一个含有数字的文件名 ruby: r 'puts `ls`[/.*\d.*/]' 引号、反撇号嵌套,还有转义和通配符,输入起来很别扭,自己理解也略吃力 bash: ls -1 | grep -m 1 -e '.*[0-9].*' 虽然命令长了一些,但少了反撇号和转义,输入更顺手,理解也更容易一些。当然,我更喜欢写成这样: ls |grep '[0-9]' |head -n1 2. 替换所有文件名中的 y 为 30 个 o bash: ls -1 | sed s/y/oooooooooooooooooooooooooooooo/ ruby: ls -1 | rp '$_.gsub! /y/, "o" * 30' 我觉得打命令的时候,bash 版的写法明显更顺手,重复内容多打几次并不难。 实际操作过程中,sh 无处不在,而其他语言确因环境而异,各种不统一。就我这周的情况来说,操作过如下环境:自己和别人的 Terminal(Mac)、公司生产环境(rh 及 CentOS 都有)、自己的 vps(Arch)、测试机、朋友的阿里云服务器(Ubuntu)。用的都是各种 sh,不依赖 alias 和环境配置,毫无压力。 |
25
ysmood OP @RemRain 是的,只要入口点是 shell 就无法避免引号的问题,这个就算用 grep sed 之类的也是很头疼的问题,比如我第三个例子里有文件的文件名本身含有引号和空格的时候,grep sed 需要再额外处理一次转义,这个时候反而能体现出一般脚本语言的优势。
我举 ruby 的例子大概是希望知道 ruby 基础知识的人能不费力的看懂单引号内的代码,外围的 r '' 是固有代码,所以不会是视觉中心。这里面的代码是不需要任何多余转义的,跟正常 ruby 一样。 比如第一个里面的 puts `ls`[/.*\d.*/] 这一段体现了很多 ruby 有意思的特性,比如类似 subshell 的撇号 `ls` 能返回系统命令的 stdout 到一个 string 变量,然后这个 string 变量可以在数组运算符里写 正则 [/.*\d.*/] 来选择想要的部分,这设计得非常合乎常理,任何一个人都应该能感到这种统一的简洁性。 第二个 $_.gsub! /y/, "o" * 30 这个例子也更能体现有意思的地方,比如 $_ 就是我们熟悉的 shell 变量,ruby 里也有,gsub 用于替换也是很常见的命名方式,一个字符串乘以数字代表字符串重复 n 次:“o” * 30 (python 支持这种写法)。这些表现都比 shell 的写法更合乎人的一般思维,不是吗? 第三个要真的写 shell,转义文件名里的特殊字符都需要写费心写一段转义处理。这边一个 F.mv a, b 就解决了这个问题。由于是内核的 mv 方法,传两个字符串进去,即使字符里有再多的空格,单双引号,反斜线,也完全不用像 shell 脚本那样绕来绕去。 配置环境我都是跑一个脚本自动 deploy 到各个机器的,不费神的,也不是什么特别不能夸平台的语言或配置。甚至在 Windows 里,没有 grep,sed 等工具的情况下他们也能正常使用,毕竟 WIndows 里装一个 ruby 比装一套 cygwin 之类工具可能更省时又少 bug。 |
26
rail4you 2015-01-11 16:38:52 +08:00
楼主的方案必须熟悉ruby才行,其实ruby和python都不太适合写one line,这是awk和sed的强项。
alias rp="r_init -p -e" ls -1 | rp '$_.gsub! /y/, "o" * 30' 这个例子很明显,不懂ruby的用户很难理解代码的意义。 ls -1 | sed s/y/oooooooooooooooooooooooooooooo/ sed就算写30个o,我也能看明白。 awk和sed相当成熟,很多linux的教程都有关于awk和sed的示例,熟悉一下规则,就能写出简单实用的命令组合。 ruby和python适合写完整脚本,个人感觉python的语法和标准库更好用一些。 |
28
ysmood OP @rail4you 我在文章后面加了一条附言,可能误解了我的文意。
ruby 和 python 不同,是适合写 one line 的,它有 do 和 end 关键字,不受缩进等格式限制。而且函数式编程大部分问题是 chain each map reduce 来解决的,常规问题 one line + 强大的原生库都能解决。 python 党的话可以看看第 18 条回复,是非常不错的 one line 选择。 |
30
rail4you 2015-01-12 11:58:28 +08:00
ruby不适合写one line,python也一样。我后面说的是两者写完整脚本都不错。
one line要求编程语言精炼,awk和sed在此方面无可比拟,perl也是一样,设计思路都是简洁大于一切。ruby和python能完成one line工作,但实用性上差太多。 文章标题,脱离这个词语气太强了,shell里很难脱离awk,sed等基础工具。后面的回复基本都在争论语言特性,你也浪费精力维护你的观点。 你换个说法就好多了,例如ruby或者python如何完成awk或者sed的工作?。 |
31
ysmood OP @rail4you 嗯,有道理,标题确实应该换一下。可惜 v2ex 不能像 stackoverflow 或 quora 那样随时更换标题,发布五分钟之后就 lock 了。
|
32
twinsant 2015-06-11 14:20:22 +08:00
作为泡CPUG的爱好者来提示:如果用Python的话,可以用iPython
|