為什麼優秀的程式設計師喜歡命令列?
優秀的程式設計師
要給優秀的程式設計師
下一個明確的定義無疑是一件非常困難的事情。擅長抽象思維、動手能力強、追求效率、喜歡自動化、願意持續學習、對程式碼質量有很高的追求等等,這些維度都有其合理性,不過又都略顯抽象和主觀。
我對於一個程式設計師是否優秀,也有自己的標準,那就是TA對命令列的熟悉/喜愛程度。這個特點可以很好的看出TA是否是一個優秀的(或者潛在優秀的)程式設計師。我周圍就有很多非常牛的程式設計師,無一例外都非常擅長在命令列中工作。那什麼叫熟悉命令列呢?簡單來說,就是90%的日常工作內容可以在命令列完成。
當然,喜歡/習慣使用命令列可能只是表象,其背後包含的實質才是優秀的程式設計師之所以優秀的原因。
自動化
Perl
語言的發明者Larry Wall
有一句名言:
The three chief virtues of a programmer are: Laziness, Impatience and Hubris. – Larry Wall
懶惰
(Laziness)這個特點位於程式設計師的三大美德
之首:唯有懶惰才會驅動程式設計師儘可能的將日常工作自動化起來,解放自己的雙手,節省自己的時間。相比較而言,不得不說,GUI
應用天然就是為了讓自動化變得困難的一種設計(此處並非貶義,GUI有著自己完全不同的目標群體)。
GUI更強調的是與人類的直接互動:通過視覺手段將資訊以多層次的方式呈現,使用視覺元素進行指引,最後系統在後臺進行實際的處理,並將最終結果以視覺手段展現出來。
這種更強調互動
過程的設計初衷使得自動化變得非常困難。另一方面,由於GUI是為互動而設計的,它的響應就不能太快,至少要留給操作者反應時間(甚至有些使用者操作需要人為的加入一些延遲,以提升使用者體驗)。
程式設計師的日常工作
程式設計師除了寫程式碼之外,還有很多事情要做,比如自動化測試、基礎設施的配置和管理、持續整合/持續釋出環境,甚至有些團隊還需要做一些與運維相關的事情(線上問題監控,環境監控等)。
- 開發/測試
- 基礎設施管理
- 持續整合/持續釋出
- 運維(監控)工作
娛樂
而這一系列的工作背後,都隱含了一個自動化
的需求。在做上述工作時,優秀的程式設計師會努力將其自動化,如果有工具就使用工具;如果沒有,就開發一個新的工具。這種努力讓一切都儘可能自動化起來的哲學起源於UNIX世界。
而UNIX哲學的實際體現則是通過命令列來完成的。
Where there is a shell, there is a way.
UNIX程式設計哲學
關於UNIX哲學,其實坊間有多個版本,這裡有一個比較詳細的清單。雖然有不同的版本,但是有很多一致的地方:
- 小即是美
- 讓程式只做好一件事
- 儘可能早地建立原型(然後逐步演進)
- 資料應該儲存為文字檔案
- 避免使用可定製性低下的使用者介面
審視這些條目,我們會發現它們事實上促成了自動化一切的可能性。這裡列舉一些小的例子,我們來看看命令列工具是如何通過應用這些哲學來簡化工作、提高效率的。一旦你熟練掌握這些技能,就再也無法離開它,也再也忍受不了低效而複雜的各種GUI
工具了。
命令列如何提升效率
一個高階計算器
在我的程式設計生涯早期,讀過的最為振奮的一本書是《UNIX程式設計環境》,和其他基本UNIX世界的大部頭比起來,這本書其實還是比較小眾的。我讀大二的時候這本書已經出版了差不多22年(中文版也已經有7年了),有一些內容已經過時了,比如沒有返回值的main函式、外接的引數列表等等,不過在學習到HOC(High Order Calculator)的全部開發過程時,我依然被深深的震撼到了。
簡而言之,這個HOC
語言的開發過程需要這樣幾個元件:
- 詞法分析器
lex
- 語法分析器
yacc
- 標準數學庫
stdlib
另外還有一些自定義的函式等,最後通過make
連線在一起。我跟著書上的講解,對著書把所有程式碼都敲了一遍。所有的操作都是在一臺很老的IBM的ThinkPad T20上完成的,而且全部都在命令列中進行(當然,還在命令列裡聽著歌)。
這也是我第一次徹底被UNIX的哲學所折服的體驗:
- 每個工具只做且做好一件事
- 工具可以協作起來
- 一切面向文字
下面是書中的Makefile
指令碼,通過簡單的配置,就將一些各司其職的小工具協作起來,完成一個程式語言程式的預編譯、編譯、連結、二進位制生成的動作。
YFLAGS = -d OBJS = hoc.o code.o init.o math.o symbol.o hoc5: $(OBJS) cc $(OBJS) -lm -o hoc5 hoc.o code.o init.o symbol.o: hoc.h code.o init.o symbol.o: x.tab.h x.tab.h: y.tab.h -cmp -s x.tab.h y.tab.h || cp y.tab.h x.tab.h pr: hoc.y hoc.h code.c init.c math.c symbol.c @pr $? @touch pr clean: rm -f $(OBJS) [xy].tab.[ch]
雖然現在來看,這本書的很多內容已經過期(特別是離它第一次出版已經過去了近30年),有興趣的朋友可以讀一讀。這裡有一個Lex/Yacc的小例子的小例子,有興趣的朋友可以看看。
當然,如果你使用現在最先進的IDE(典型的GUI工具),其背後做的事情也是同樣的原理:生成一個Makefile,然後在幕後呼叫它。
基礎設施自動化
開發過程中,工程師還需要關注的一個問題是:軟體執行的環境。我在學生時代剛開始學習Linux的時候,會在Windows機器上裝一個虛擬機器軟體VMWare,然後在VMWare中安裝一個Redhat Linux 9
。
這樣當我不小心把Linux玩壞了之後,只需要重灌一下就行了,不影響我的其他資料(比如課程作業、文件之類)。不過每次重灌也挺麻煩,需要找到iso
映象檔案,再掛載到本地的虛擬光碟機上,然後再用VMWare來安裝。
而且這些動作都是在GUI裡完成的,每次都要做很多重複的事情:找映象檔案,使用虛擬光碟機軟體掛載,啟動VMWare,安裝Linux,配置個人偏好,配置使用者名稱/密碼等等。熟練之後,我可以在30 - 60分鐘內安裝和配置好一個新的環境。
Vagrant
後來我就發現了Vagrant,它支援開發者通過配置的方式將機器描述出來,然後通過命令列的方式來安裝並啟動,比如下面這個配置:
VAGRANTFILE_API_VERSION = "2" Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| config.vm.box = "precise64" config.vm.network "private_network", :ip => "192.168.2.100" end
它定義了一個虛擬機器,使用Ubuntu Precise 64
的映象,然後為其配置一個網路地址192.168.2.100
,定義好之後,我只需要執行:
$ vagrant up
我的機器就可以在幾分鐘內裝好,因為這個動作是命令列裡完成的,我可以在持續整合環境裡做同樣的事情 – 只需要一條命令。定義好的這個檔案可以在團隊內共享,可以放入版本管理,團隊裡的任何一個成員都可以在幾分鐘內得到一個和我一樣的環境。
Ansible
一般,對於一個軟體專案而言,一個全新的作業系統基本上沒有任何用處。為了讓應用跑起來,我們還需要很多東西。比如Web伺服器、Java環境、cgi路徑等,除了安裝一些軟體之外,還有大量的配置工作要做,比如apache httpd
伺服器的文件根路徑,JAVA_HOME
環境變數等等。
這些工作做好了,一個環境才算就緒。我記得在上一個專案上,不小心把測試環境的Tomcat目錄給刪除了,結果害的另外一位同事花了三四個小時才把環境恢復回來(包括重新安裝Tomcat,配置一些JAVA_OPTS,應用的部署等)。
不過好在我們有很多工具可以幫助開發者完成環境的自動化準備,比如:Chef、 Puppet、Ansible。只需要一些簡單的配置,然後結合一個命令列應用,整個過程就可以自動化起來了:
- name: setup custom repo apt: pkg=python-pycurl state=present - name: enable carbon copy: dest=/etc/default/graphite-carbon content='CARBON_CACHE_ENABLED=true' - name: install graphite and deps apt: name={{ item }} state=present with_items: packages - name: install graphite and deps pip: name={{ item }} state=present with_items: python_packages - name: setup apache copy: src=apache2-graphite.conf dest=/etc/apache2/sites-available/default notify: restart apache - name: configure wsgi file: path=/etc/apache2/wsgi state=directory
上邊的配置描述了安裝graphite-carbon
、配置apahce
等很多手工的勞動,開發者現在只需要執行:
$ ansible
就可以將整個過程自動化起來。現在如果我不小心把Tomcat
刪了,只需要幾分鐘就可以重新配置一個全新的,當然整個過程還是自動的。這在GUI下完全無法想象,特別是在有如此多的定製內容的場景下。
持續整合/持續釋出
日常開發任務中,除了實際的編碼和環境配置之外,另一大部分內容就是持續整合/持續釋出了。藉助於命令列,這個動作也可以非常高效和自動化。
Jenkins
持續整合/持續釋出已經是很多企業IT的基本配置了。各個團隊通過持續整合環境來編譯程式碼、靜態檢查、執行單元測試、端到端測試、生成報告、打包、部署到測試環境等等。
比如在Jenkins
環境中,在最前的版本中,要配置一個構建任務需要很多的GUI操作,不過在新版本中,大部分操作都已經可以寫成指令碼。
這樣的方式,使得自動化變成了可能,要複製一個已有的pipline
,或者要修改一些配置、命令、變數等等,再也不需要用滑鼠點來點去了。而且這些程式碼可以納入專案程式碼庫中,和其他程式碼一起被管理、維護,變更歷史也更容易追蹤和回滾(在GUI上,特別是基於Web的,回滾操作基本上屬於不可能)。
node { def mvnHome stage('Preparation') { // for display purposes git 'https://github.com/jglick/simple-maven-project-with-tests.git' mvnHome = tool 'M3' } stage('Build') { sh "'${mvnHome}/bin/mvn' -Dmaven.test.failure.ignore clean package" } stage('Results') { junit '*/target/surefire-reports/TEST-.xml' archive 'target/*.jar' } }
上面這段groovy
指令碼定義了三個階段,每個階段中分別有自己的命令,這種以程式碼來控制的方式顯然比GUI編輯的方式更加高效,自動化也變成了可能。
運維工作
自動化監控
Graphite是一個功能強大的監控工具,不過其背後的理念倒是很簡單:
- 儲存基於時間線的資料
- 將資料渲染成圖,並定期重新整理
使用者只需要將資料按照一定格式定期傳送給Graphite
,剩下的事情就交給Graphite
了,比如它可以消費這樣的資料:
instance.prod.cpu.load 40 1484638635 instance.prod.cpu.load 35 1484638754 instance.prod.cpu.load 23 1484638812
第一個欄位表示資料的名稱,比如此處instance.prod.cpu.load
表示prod
例項的CPU負載,第二個欄位表示資料的值,最後一個欄位表示時間戳。
這樣,Graphite
就會將所有同一名稱下的值按照時間順序畫成圖。
預設地,Graphite
會監聽一個網路埠,使用者通過網路將資訊傳送給這個埠,然後Graphite
會將資訊持久化起來,然後定期重新整理。簡而言之,只需要一條命令就可以做到傳送資料:
echo "instance.prod.cpu.load 23 </span>date +%s<span class="pl-pds">" | nc -q0 graphite.server 2003
date +%s
會生成當前時間戳,然後通過echo
命令將其拼成一個完整的字串,比如:
instance.prod.cpu.load 23 1484638812
然後通過管道|
將這個字串通過網路傳送給graphite.server
這臺機器的2003
埠。這樣資料就被記錄在graphite.server
上了。
定時任務
如果我們要自動的將資料每隔幾秒就傳送給graphite.server
,只需要改造一下這行命令:
- 獲取當前CPU的load
- 獲取當前時間戳
- 拼成一個字串
- 傳送給
graphite.server
的2003
埠 - 每隔5分鐘做重複一下1-4
獲取CPU的load在大多數系統中都很容易:
ps -A -o %cpu
這裡的引數:
-A
表示統計所有當前程式-o %cpu
表示僅顯示%cpu
列的數值
這樣可以得到每個程式佔用CPU負載的數字:
%CPU 12.0 8.2 1.2 ...
下一步是將這些數字加起來。通過awk
命令,可以很容易做到這一點:
$ awk '{s+=$1} END {print s}'
比如要計算1 2 3
的和:
$ echo "1\n2\n3" | awk '{s+=$1} END {print s}' 6
通過管道可以講兩者連起來:
$ ps -A -o %cpu | awk '{s+=$1} END {print s}'
我們測試一下效果:
$ ps -A -o %cpu | awk '{s+=$1} END {print s}' 28.6
看來還不錯,有個這個指令碼,通過crontab
來定期呼叫即可:
#!/bin/bash SERVER=graphite.server PORT=2003 LOAD=</span>ps -A -o %cpu <span class="pl-k">|</span> awk <span class="pl-s"><span class="pl-pds">'</span>{s+=$1} END {print s}<span class="pl-pds">'</span></span><span class="pl-pds"> echo "instance.prod.cpu.load ${LOAD} </span>date +%s<span class="pl-pds">" | nc -q0 ${SERVER} ${PORT}
當然,如果使用Grafana等強調UI的工具,可以很容易的做的更加酷炫:
想想用GUI應用如何做到這些工作。
娛樂
命令列的MP3播放器
最早的時候,有一個叫做mpg123
的命令列工具,用來播放MP3檔案。不過這個工具是商用的,於是就有人寫了一個工具,叫mpg321
,基本上是mpg123
的開源克隆。不過後來mpg123
自己也開源了,這是後話不提。
將我的所有mp3
檔案的路徑儲存成一個檔案,相當於我的歌單:
$ ls /Users/jtqiu/Music/*.mp3 > favorites.list $ cat favorites.list ... /Users/jtqiu/Music/Rolling In The Deep-Adele.mp3 /Users/jtqiu/Music/Wavin' Flag-K'Naan.mp3 /Users/jtqiu/Music/藍蓮花-許巍.mp3 ...
然後我將這個歌單交給mpg321
去在後臺播放:
$ mpg321 -q --list favorites.list & [1] 10268
這樣我就可以一邊寫程式碼一邊聽音樂,如果聽煩了,只需要將這個後臺任務切換到前臺fg
,然後就可以關掉了:
$ fg [1] + 10268 running mpg321 -q --list favorites.list
小結
綜上,優秀的程式設計師藉助命令列的特性,可以成倍(有時候是跨越數量級的)提高工作效率,從而有更多的時間進行思考、學習新的技能,或者開發新的工具幫助某項工作的自動化。這也是優秀的程式設計師之所以優秀的原因。而面向手工的、原始的圖形介面會拖慢這個過程,很多原本可以自動化起來的工作被淹沒在“簡單的GUI”之中。
最後補充一點,本文的關鍵在於強調優秀的程式設計師
與命令列的關係,而不在GUI程式和命令列的優劣對比。GUI程式當然有其使用場景,比如做3D建模、GIS
系統、設計師的創作、圖文並茂的字處理軟體、電影播放器、網頁瀏覽器等等。
應該說,命令列
和優秀的程式設計師
之間更多是關聯關係,而不是因果關係。在程式設計師日常的工作中,涉及到的更多的是一些需要命令列工具來做支援的場景。如果走極端,在不適合的場景中強行使用命令列,而置效率於不顧,則未免有點矯枉過正,南轅北轍了。
相關文章
- 為什麼程式設計師喜歡在深夜程式設計?程式設計師
- 程式設計師為什麼喜歡夜間工作?程式設計師
- 為什麼程式設計師喜歡熬夜工作?程式設計師
- 程式設計師為什麼喜歡程式設計這項工作?程式設計師
- 我為什麼喜歡程式設計程式設計
- 程式設計師為什麼喜歡在晚上編碼程式設計師
- 為什麼程式設計師喜歡在半夜寫程式碼?程式設計師
- 為什麼程式設計師喜歡深更半夜寫程式碼程式設計師
- 為什麼很多人都不喜歡做程式設計師?程式設計師
- 為什麼我喜歡單獨程式設計程式設計
- 趣文:為什麼有些程式設計師特別喜歡貓?程式設計師
- 程式設計師週末都喜歡做什麼?程式設計師
- 程式設計師喜歡什麼樣的產品經理?程式設計師
- 為啥程式設計師喜歡Android?程式設計師Android
- 為什麼我喜歡程式設計 程式設計充滿了樂趣程式設計
- 程式設計師喜歡Linux系統的原因是什麼?程式設計師Linux
- 同樣是程式設計師,為什麼別人比你更優秀?程式設計師
- 設計師更喜歡什麼作業系統作業系統
- 為什麼程式設計師會有最喜歡與最討厭的程式語言?(earthly)程式設計師
- 優秀程式設計師的優秀歷程程式設計師
- 什麼原因成就了一位優秀的程式設計師?程式設計師
- 為什麼我喜歡 Lisp 程式語言Lisp
- 你想成為優秀的Java程式設計師嗎?Java程式設計師
- 為什麼我喜歡JavaJava
- 程式設計師被人喜歡的13點原因程式設計師
- Java程式設計師如何成為優秀的架構師Java程式設計師架構
- 怎麼成為一個優秀的程式設計師 而不是一個優秀的碼農?程式設計師
- 優秀程式設計師因何而優秀?程式設計師
- 程式猿為什麼不招妹子喜歡的原因
- 我喜歡程式設計師這份差事!程式設計師
- 如何理解Linus Torvalds的“什麼才是優秀程式設計師”的話程式設計師
- 優秀程式設計師不一定是優秀的軟體設計師程式設計師
- 成為優秀程式設計師的10個有效方法程式設計師
- 成為優秀Java程式設計師的10大技巧Java程式設計師
- 成為優秀程式設計師的十個有效方法程式設計師
- 成為優秀程式設計師的十個Tips程式設計師
- 如何成為一個優秀的 JavaScript 程式設計師JavaScript程式設計師
- 成為優秀程式設計師的最佳學習方式程式設計師