是程序员就用命令行

作为程序员,熟练掌握命令行程序的使用是感受 unix 编程艺术的最便捷的方法,同时还能额外对日常工作提效。

为什么要用命令行

程序员和普通人的区别在哪里?

程序员:会复用软件

复用,不是使用!

使用:在软件设定好的范围里面活动,会使用 office 软件,会使用浏览器浏览网,无法脱离这个固定的范围。

复用:在于使用 现有” 的软件创造出 新” 的功能来,广阔天地大有作为。

命令行是程序员最方便的复用阵地

考题:不写代码,可以使用任何软件,4 分钟内做到下面的事情,可能吗?

•查找 node_modules 下面,所有包含 createElement 的 *.mini.js 文件•统计代码库中 import 的单词出现的次数•列出当前目录最近三天有修改的所有文件•下载 www.test.com/images/001.png .. www.test.com/images/999.png 的图片,并且并发控制 3

如果你做不到,一起读完这篇文章吧。

参考答案:
# 查找 node_modules 下面 所有包含 createElement 的 *.mini.js 文件
find node_modules -name '*.min.js'   \
     -exec grep -w createElement {} \;
# 统计代码库中 import 的单词出现的次数
git grep -c import  |  \ 
awk -F':' '{sum+=$2} END{print sum}'
# 列出代码库中最近三天有修改的所有文件
find . -mtime -3d
# 下载 www.test.com/images/001.png .. 
# www.test.com/images/999.png  的图片,
# 并且并发控制 3
seq  -f 'http://www.test.com/images/%03g.png'  1 999   |  \
parallel  -j 3 -I{} curl -O {}

1个基本思想:管道

管道的思想,上一个命令的输出是下一个命令的输入,像一条流水线,所有的命令可以通过管道的形式自由的组合;而每个命令只专注自己最核心的事情,这就是 Do one thing,and do it well

比如 ls 要输出炫酷而无用的彩色列表

/bin/ls 命令本身就负责输出文件的列表功能,只和文件系统交互,把目录信息的文本输出到标准输出。

而需要对输出的文本做着色是另外一个命令的事情,它只负责对标准输入中传来的字符在终端中做着色,这样的命令有很多,我用的就是 lolcat

通过管道 /bin/ls -l | lolcat 就能非常容易实现上面的效果,而且通过 lolcat 你可以去“装饰”任何你想 lol 下的东西。

这就是管道带来的自由和灵活。

一个小知识点

命令行程序有了标准输入(stdin),标准输出(stdout),为什么还有标准错误输出(stderr)呢? 错误输出有啥用呢?stdin,stdout 都是命令程序对外获得输入和输出的有效重要信息的接口,如果想给用户输出一些不那么重要的信息,那就是标准错误输出了。

换句话说 stderr 是命令程序的“GUI”,比如下面的 curl 命令,红色框中的内容其实就是输出到stderr 中的, 用来告诉用户现在网络请求完成多少进度;而真正重要的信息 curl 获取的页面网络信息还是输出从 stdout 中去,管道给到下一个命令程序 head 使用。

几个基石

上面讲到了使用命令行的基本思想是通过管道来组合,但是用什么来组合呢?这里辟殊介绍下我认为常用且有用的几个命令作为组合的基石。

利器

echo

一个被低估的命令,其实他是命令行中的 console.log,你有疑惑就靠它打印下来解决。

比如上面需要并发下载的问题,你对 paralle 需要执行的命令不确定的时候可以这样来,看看命令生成的对不对。

# 注意看 echo 的位置
seq  -f 'http://www.test.com/images/%03g.png'  1 999   | \
parallel  -j 3 -I{} echo curl -O {}
#输出
...
curl -O http://www.test.com/images/179.png
curl -O http://www.test.com/images/180.png
curl -O http://www.test.com/images/181.png
curl -O http://www.test.com/images/182.png
...
xargs
f(g(x))

光光有管道 (pipe)是不够的,有时候还有把命令行程序组合下 compose , 即把上一个命令的结果作为下一个命令的输入参数

比如要删除 3 天前的日志,

find  /Users/pshu/.shelllogs -mtime +3 | \
xargs rm -rf

假如 find /Users/pshu/.shelllogs 输出的是

1.log
2.log
3.log

那这个命令组合最终的执行效果就是

rm -rf 1.log 2.log 3.log

find 命令的输出变成了 rm 命令的参数输入,有了这个能力,我们就能组合出更多有用的功能。

tee

命令行中的管道中的三通(其实就是个 T 字型的水管),把标准输入原封不动的给到标准输出的同时,把标准输入写到指定的文件中。

使用的场景主要是保存下中间结果,命令调试的时候非常适合使用,比如我要从一个网络延迟很大的地方下载文件分析, 那命令的大部分时间在下载,每次调试都要浪费下载的时间。

curl  http://veryslow.com  | grep keyword

这种情况可以先用 tee 命令保留下来,这样调试方便许多。

curl  http://veryslow.com  |  \
    tee web.log | \
    grep keyword
cat web.log | grep keyword

文件

这些命令比较常用,网络资料也比较多,就说下屁叔认为必知必回的几个命令和对应的选项

find

文件查找的利器。经常看到一些同学在文件查找的时候都一般借助 vscode 的搜索功能,确实好用。而我不用他的三个原因供大家参考下

1.速度一般:因为他的背后就是 find ,再消耗一些进程通信回到 vscode,最多和 find 一样快2.定制化搜索能力太差,比如刚才的例子 查找node_modules下面所有包含 createElement 的 *.mini.js 文件; vscode 铁粉可以来指导下怎么搞。3.不方便分享,命令行本身就是文本,复制黏贴下能把相同的操作分享给别人,但是用 IDE 或者编辑器你就得再一番描述下,甚至截图。

好了不吹了。开始介绍 find命令。它的基本使用格式 find 目录 [各种选项],默认输出列出搜索到的文件。

重点选项

-name 指定搜索的文件名字,支持正则,比如 -name 'package.json' 搜所有 package.json 文件;-name '*.tsx' 所有 tsx 文件。•-type 执行搜索类型。-type f搜文件类型, -type d搜目录类型•-maxdepth n,搜索目录层数深度,比如 -maxdepth 3 只搜 3 级目录,这种一般用来缩小下 find 的搜索范围,验证下命令搜的对不对•-exec 用来指定,搜了文件怎么处理;比如 find node_modules -name '*.min.js' -exec grep -wl createElement {} ; 这里就是搜到的文件后,再查找下有没有包含 createElement, 那个 {} 就是文件名的占位符。\; 表示命令结束。使用 -exec 后 find 的输出就是 exec 中命令的输出了,注意下。•-mtime 指定查询文件修改时间,比如 -mtime +3d 就表示查找3 天之前修改(moddify)过的文件。

grep

正则查找的利器,目前各种新语言出现也不少的复刻它的版本,说是性能提升少,大家有兴趣可以试试其他的;但是这个经典的一点要学。

grep [选项] 正则表达 文件名 or grep [选项] 正则表达 查找标准输入。grep 会打印匹配满足正则的的内容;所以如果你用 grep 搜一个 minify 过的文件,会打出超长的内容,所以作为前端需要 grep 压缩的过的文件最好先通过 prettier 格式化下。

重点选项

-c 接触文件中命中正则的次数;常用来统计•-v 显示不符合正则的内容,即反查•-n 显示匹配位置的行号;方便根据搜索结果调整到对应的行号•-l 只显示匹配文件名•-H 在每条匹配记录前打印文件名

cat/head/tail/less/more

这几个命令很常见,各种资料很多这里简单列下辟殊用他们的场景。

cat

一般情况下用来打印下长度较小的文件,还有一个作用就是用来和合并(concat)文件输出到命令行。配合重定向就能方便的合并文件,cat style1.css style2.css > style.css 就是把 两个 style 文件合并成一个。

head/tail

查看文件头部和头部的信息,主要用来简单的预览;配合 -n 控制显示的行数。tail 还有一个 -f 选项一直跟随文件变化,常常用来观察日志用的。

less

类型 vim 的方式来查看文件内容,j 相当于下移,k上移,q 退出,如果你熟悉 vim 的话一切都这么自然。

网络

curl

网络请求是yyds,NASA都在用的网络请求程序。对于前端开发了说的话,curl 可以方便的下载 http 资源,页面,脚本,图片, json 文件等等。常用的方式就是 curl http://test.com/test.png 发起一个GET请求, 响应的内容会直接打印在标准输出上;为了方便存储内容可以用 -o filename.ext 来指定下载内容存储的文件名,也可以用 -O 大写的O,直接使用 url 路径中的文件名,curl http://test.com/test.png -O 就直接存成了 test.png

当然仅仅能发 GET 请求是不够的,通过 curl -X POST 的方式还能发起POST、DELETE、HEAD、PUT 请求。比如要发起一个 JSON 的 POST 请求需要这样( -H是指定 HTTP 请求头)。

curl -X POST \ 
     -H "Content-type: application/json" \ 
     -d '{"test":1}'  \
     'http://www.test.com'

显然这么手打是很低效的,而且记忆这么多选项也累,所以辟殊会借助一些 builder 来构建一些 http 请求, 比如这个 https://tools.w3cub.com/curl-builder ,大家也可以找一个适合自己的。同时有时候调试 http 服务的时候,还可以利用 chrome 网络面板中的复制功能直接得到一个复杂的请求 curl 命令,美滋滋,所以调试 api 的时候就不用需要通过刷网页了,直接在终端发起就行了。

为什么要写这篇文章

首先,真的可以提效,写 10 分钟的node.js 的脚本的功能,可能用命令行组合下1分钟不到,存成别名,以后复用乐呵呵,爽歪歪。

再次,就是我们组的年轻程序员很多,对计算机历史中智慧的结晶存在一点误解,所以讲讲这些命令行的东西,引出历史,促进更好的创新。在命令的设计理念里面有很多思想值得我们在前端实践的时候解决和参考。

单一职责 single responsibility

比如命令行程序每个功能都是一个很小很内聚的程序,因为它很小很原子,就很方便复用和组合。再反观我们的前端实践中的 react 组件,是不是一个单一职责的一个组件,显然我们更需要单一职责组件这样就方便我们复用和组合。

组合优于继承 compose over inheritance

通过管道的方式可以和命令行程序灵活的组合起来,扩展出新的功能。当你想实现一个新的功能的时候,不需要修改现有的命令行程序,通过管道来组合就行了。同样我们的组件是不是能够通过组合来扩展新功能,而不是在组件中再增加一个 if-else来实现。这也是我们在进行组件设计时候需要考虑的问题。

声明式优于命令式 declaritive over imperative

命令行虽然是“命令”行,但它的表达从来就是声明式的,我需要查找一个文件,find . -name 'app.js' , 只声明清楚你声明你要做什么(当前目录查找,名字为 app.js的文件)。但是从来关心如何查找。在类比我们前端的组件,如果你修改页面的 titlebar 的状态,是通过 useEffect 在 hook 中,命令式的去控制,还是再封装一个 TitleBar 的组件,

<Titlebar  transparent={"auto"}  
           title={"标题文字"} /> 

来声明式定义 TitleBar 呢。

这些好的设计思想的事件都可以通过长期使用命令程序来潜移默化自己的开发思维中去。所以使用命令行不只是提效,通过命令行的实践来进行开发思维的体操,时时刻刻打磨自己的设计思想。

这篇文章是我半年前(也可能更久前)看一个同事笨拙的使用命令行时,答应他要写的,终于没有太监写完了!

欢迎转发赞赏

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值