工具(-1): 命令行

典型使用场景

  • 服务器远程登陆,只有命令行界面,此时不得不用命令行工具处理日常工作任务,所以就用,边用边查手册,用久了所以熟悉。
  • 自己的电脑上经常使用命令行处理日常编程/文件管理工作。
  • 自己编写命令行程序。

使用环境

分类随记

文件上传、下载:


**命令**:sz,rz,scp,rsync,wget,curl

评注

  • for循环wget,分分钟多进程下载。
  • wget -c 分分钟断点续传。

压缩、解压:


命令:tar,zip,unzip

评注
所以得会用tar,zip等命令。我一开始的时候,问一个老手tar cvzf,tar xvzf后面一排参数啥意思,老手说:”早就忘记了,天天都这么用“。所以我认为它和一个按钮一个菜单没什么区别,命令行参数的选项和参数一般是经常用就熟悉,久不用就容易忘记,此时复查是正常的事情。顺便吐槽下windows下7z的菜单里直接解压tar.gz要解压两次真是麻烦事,命令行当然可以一次搞定。

目录和文件的创建、移除、拷贝、移动:


命令:mkdir,rm,mv,cp

评注:
文件拷贝,主要是要考虑很多意外情况,例如

  • 非空目录的移除,
  • 递归删除时拼接路径的安全隐患(递归删除根目录就一点都不好玩了)
  • 删除时文件被占用
  • 如果可能只是短时被占用,可以sleep几十毫秒再删,重试n次退出。
  • 考虑采用”删除xxx.old, 移动xxx到xxx.old, 拷贝yyy到xxx“的方式代替”直接删除xxx,移动yyy到xxx“的方案、
  • 理解硬盘驱动,冗余磁盘阵列,inode,文件和目录,缓存策略等。

文本显示、查找、替换、统计:


命令:cat,tail,less,head,grep,sed,awk,wc,xargs

评注:
在命令行下分析程序的日志,必备的技能就是文本操作。这组命令行基本要通过管道来配合使用。我个人觉的awk的设计有点古老,awk的脚本我是一点都不觉得写起来流畅和爽快,sed的稍微好点,但也一般。有好多次,我都尝试把awk学下来,不过都没成功,简单说规则一致性太差了。

如果是在windows下,这种流式过滤的工作,可以开LinqPAD写Linq来做,高级语言的代码可读性更好,也更利于维护,shell脚本的可读性差很多。考虑到夸平台,选择python脚本或者nodejs脚本都是不错的选择,python基本上什么库都有,nodejs 可能库不如python那么全,这两个工具链也适合把常用工具库发布到社区仓库上。

很多shell脚本写的人天马行空,别人读起来就不好玩了,毕竟写shell的人不写注释,遍地意大利面条。吐槽归吐槽,现实上我们不得不妥协,该用还得用。毕竟一切数据都可以输出成文本,所以文本的流式分析是基本技能。

我认为这组命令的组合使用能让人体会到管道的好处。管道让多进程之间的流式操作流畅进行。顺便我认为shell的一个特点是,它天生是多进程程序,一个shell里随便一堆命令就是多进程操作,可能很多人写着的时候并没有意识到这点。进一步,多主机之间ssh授权后,shell里的ssh远程命令的组织又天生带有点分布式属性。

**[非对称加密&反连协议] **:

ssh也许这能带来一些启发,例如ssh 信任就是非对称加密。

  • A机留私钥; B机在.ssh目录放公钥,在.ssh/authorized_keys里填上公钥内容。
  • 则A机执行ssh user@B cmd 的时候,B机会用公钥和A机之间建立ssh连接,然后执行命令。
  • git 的客户端向git的仓库之间建立ssh授权也是这个过程,实际上任何依赖于ssh建立授权的都是这个过程。
  • 这实际上是一种“反连”协议,思考下私钥在哪里就知道了。

[fork&exec]:
理解管道的另一个要素是,需要理解fork,vfork、clone,exec;需要理解标准输入输出。fork之后,在exec子进程之前重定向父进程的标准输出,重定向子进程的标准输入,这样它们就被流式串在一起。

文本编辑:


命令:vi,vim,emacs,cat,tail

评注:

cat,tail 是命令行下快速查看文本文件的快捷命令,cat一般配合grep、head使用更佳,而tail一般用来快速查看文件末尾或者通过tail -f来监听文件的变更。

vi基本技能。shell下编辑文本只能用这个了,我认为vi或者vim里最重要的技能就三个:导航、查找、替换,带着这个思维去记忆那些命令会明确很多。我觉的任何一个程序的核心功能就几个,每个核心功能的核心命令也就几个,其他的就按需查用即可。同时,要理解vim的一个重要概念:模式。同样的键盘按钮,在不同vim模式下,操作代表不同的语义。想象一下相机是如何由有限的几个按钮做到多组不同功能的支持。

我认为快捷键并不是vim/emacs之类的最大特点,任何一个支持快捷键的编辑器,你都可以通过熟练掌握常见的快捷键使得操作快捷高效,比如notepad、visual studio、word等,理解这点有助于我们看清事实,驾驭工具。

资源监控:


**命令**: ps,du,top,htop,iotop,iostat,vmstat,lsof

评注:
输出的信息都可以配合grep等使用,比如ps aux|grep processname.

  • 任务管理器型:vmstat, top, htop,
  • 进程:ps
  • 磁盘:du, iotop, iostat
  • 文件句柄:lsof,比如查看一个进程打开的文件描述符,反向查看哪些进程打开了某个文件。

理解背后的知识:

  • CPU的虚拟化,理解OS对process的调度(Scheduling):
    • 调度算法:FIFO,SJF,STCF,RR,MLFQ
    • 理解os调度process的话,再去看协程,用户态协程需要自己做调度:)
    • IO密集和CPU密集?
  • 内存:
    • 物理内存的虚拟化
    • 进程的内存分布,内部浪费和外部浪费
    • 分页,页错误

调试和Hook:


**命令**: ulimit,gdb,strace,pstack,dtrace

评注:
开发和运维中,调试程序和Hook程序是必备的技能。

例1: coredump生成,运行时资源限制。

通过ulimit -c unlimited让程序崩溃时产生coredump,然后用gdb调试,看崩溃堆栈,变量、单步等,这是gdb的常规操作。linux下开发必备技能了。ulimit 还可以限制其他资源,比如-n限制文件句柄个数,-s限制堆栈大小等,具体查文档。

例2: C/C++开发中,每个线程的堆栈,判断出多线程问题、内存问题,这两个基本占大头,最常用的是GDB :Debugging with GDB,GDB操作的基本原则如下:

  • 查看线程->切换线程->查看堆栈->切换函数帧和代码行->查看变量信息,再结合源码合理怀疑临界区问题、状态机问题、内存问题等。
  • 定位就是:查看->切换->怀疑->[重现]->确定->修复,疑难问题可能只能靠肉眼codereview确定。

例3: 系统调用、堆栈等的Hook:strace,dtrace、pstack等。
这组算是lowlevel的神器,在很多时候能起到一针见血的效果。这里,可以理解下系统调用的原理,中断的实现原理,这样你在使用strace监控一个程序的系统调用的时候,脑子里会有更清晰的思路。系统调用并不是一个简单的函数调用这件事么..

例4: 常见的有添加路径到环境变量:export LD_LIBRARY_PATH=$PWD; 理解系统查找共享库的搜索路径顺序。

例5: 多线程程序调试中一个重要的关注点是关于锁的问题:

  • Things UNIX can do atomically 多进程之间可以对文件和文件夹加锁
  • 说起锁,想起Dijkstra,锁、信号量、等并发编程同步的机制最早都是他搞清楚的。
  • 什么是”原子“的,在CS里,这是一个及其重要的概念。
  • 锁应该保护的是数据还是行为?
  • 锁的尽量局部化。
  • 感兴趣?:Operating Systems: Three Easy Pieces

网络:


**命令**: tcpdump

评注:
tcpdump抓包是一定要掌握的,抓tcp、udp包是必备的技能。输出pcap格式文件也是要的。如果是有图形界面的话,wireshake在windows和linux下都能用,如果是windows上还可以用microsoft networkmonitor。说实话,我觉的microsoft networkmonitor做的比wireshake好用。

一个TCP连接,三次握手、四次挥手;

  • 为什么要三次握手。syn、ack、ackack,它们是原子不可分的么,在更复杂的协议里,一个syn,一个ack、一个ackack本身可能就是一组子协议。三者中任何一个环节被打破(Break),都会导致握手失败。
  • 为什么要四次挥手。自己设计流式协议的话,如果你不需要这种Feature,就可以暴力Close了。知其然,知其所以然。
  • 拥塞控制算法(Congestion Ctrol, CC),根据历史数据记录估算未来数据,这是一种学习(统计):Reno,Cubic,Ledbat,BBR。

理解网络相关的必要知识:

  • P2P打洞的原理:NAT,限ip,限port,限ip和port,随机ip和port
  • DNS的原理,host,DNS污染

建立在TCP连接之上的Http连接,HTTP request包在什么时候发,seq号是怎样变动的,要自己通过tcpdump、wireshake、microsoft-networkmonitor等实际观察过至少一次。HTTP协议说到底是一个TCP协议,这点很重要。

理解这些协议之间的演化路径:SPDY->HTTP2->QUIC,它们的基本原理和目标都是一致的。

解决复杂链路问题,一台机子有多个网卡(ifconfig),或者单网卡,多ip,绑定到0.0.0.0的时候,收发包是怎样的情况?

理解协议栈,拆开ip包头,tcp和udp包头,以及做checksum校验的拟包头,理解协议包是如何被Stack的。话说最早我想当然认为tcp包头里面才是ip包包头,然而事实上这个鸡蛋黄是上层的包头在下层协议包的包体里。不知道是否有同类人有过这种直觉的错觉。

联想
关于拥赛控制,本质上是一个对历史数据做filter,实际上Deep Learning也是一个这样的过程:问题是,它和大数据一样,广告词的成分居多,只有少数人在做和它相关的事情,从工程上来说,它应该是被搭建出来的算法模块,而非广告词,然而满大街的AI广告词让人讨厌,正经的程序员和工程师要对那些满口广告词的文章带有警惕性。

http://neuralnetworksanddeeplearning.com 这个6章的教程主要讲神经网络最基本的一个算法(误差逆传播算法,也就是所谓的bp算法)的基本原理(一部分在推导bp过程如何被用来算bp误差函数的微分,这里主要用到了微分的链式法则,求微分的目的是加速递归下降法的收敛速度),作者也对AI的未来有一番自己的见解,你会发现bp本质上就是用来做连续函数的逼近(利用递归下降法,让逼近函数的权重参数“快速”收敛)。后面的CNN只是一种用来减少bp权重参数的分层策略,本质上还是bp,不过RNN只是简单提了一下。

数据库操作:


**命令**:mysql,sqlite3,mongo,redis

评注:

  • 命令行下,操作mysql和sqlite是常见的事情,这个基本就是常规的sql操作语句。
    • 可以通过这个在线网站练习sql里窗口函数的使用:windowfunctions.com/questions/0
  • 理解关系型数据库的范式,ACID,事务(Transaction)。
  • 基于行存储Block的数据库,基于列存储Block的数据库,各自的优缺点
    • B+树
    • 局部性原理,行Block存储是行记录读写偏好,列的则是部分字段读写偏好
    • trad off,没有最好,只有最合适
  • 理解数据库元数据的读写,核心是bin日志文件的读写。
    • 理解复式记账的本质

版本管理等:


**命令**:svn,git

评注:

视角和启发

如果你觉的这么多东西真啰嗦,很简单,不理会此处的所有链接,只看个大概,我要强调的只是如何看待命令行的一种视角,命令则应该每个人自己使用中自己累积,正如一个人对自己写的代码,无论多复杂都能了然于胸,而对别人写的代码,则要花非常多心思才能做到同样熟悉,所以具体的操作应该是自己使用出来的。因为命令行作为操作系统的shell,每个命令行侧重的是操作系统的某一方面的知识,我提供的只是把命令行作为一个索引,去归类某些知识,这是我的目的。

在Mac下用了一年多fish,感觉很好用,大部分命令敲过一遍之后,输入前面几个单词,fish就会自动灰色显示备选历史命令,方向键选择下就好了,很方便。个人电脑的终端上装fish这种带自动补全功能的工具是很方便的。我们使用命令行,首先是为了效率。

以及,命令行的option,其实就像一级菜单/二级菜单一样的概念,只不过在命令行下你不能方便的使用鼠标就看清楚这些菜单是什么,就只能靠大脑去记忆,所以显得能记住命令的好像多高级,你想想如果给你VisualStudio,你会去记住那些一级菜单/二级菜单都是什么吗?不会,你只会每次都用鼠标一个个点出来而已,习惯了就记住了。命令行的option,也大概是这样的,通过查文档/常用示例,经常用的就记住了,没怎么用的估计就用一次忘记一次了。从这个角度来说,命令行的option设计上,应该满足:最少但是足够完备 的方式,最少使得你仅仅只需要记住这些必要的option就能使用,足够完备使得需要的大部分功能都有。

类比GUI,还有一种常见的GUI是应付选项特别多的情况:向导。例如IDE创建Project一般就是会有一个step by step的向导,让你一步一步选择和填写必要的设置。同理,在命令行下,也有类似的方式:交互式命令行。通过交互式命令,一步一步的提问让你选择选项和填写必要的配置,最后一次性执行。或者不执行,只是生成合并后的命令,让你自己拷贝到脚本里执行等,相当于一种交互式的文档,读文档你还是要自己根据文档写命令,交互式的方式直接通过分支裁剪你的需求生成你要的命令。这样,命令行可以通过交互式的方式提供一种更友好的「文档」,类似于游戏里的新手练习区。

命令行程序案例

A few years ago, we chose Go over Node.js for writing the wolkenkit CLI. A few months ago we rewrote everything from scratch, this time using Node.js again. We've learned that you can't dance at two weddings.

posted @ 2018-02-25 15:16  ffl  阅读(513)  评论(2编辑  收藏  举报