示範指令的引數要說明,寫成字面值的那些最不像需要說明
示範指令由四個位置組成:工具(adb)、子命令(shell、pull)、選項(-p、-d)、引數(子命令作用的對象)。文章的職責是讓讀者知道每個位置填的是什麼,而四個位置裡引數最需要寫——工具與子命令的名字讀者可以去查,選項多半有 --help 列得出來,而引數的語意常常只有文章講得出來:為什麼路徑選在這裡、兩個引數哪個是來源、給不給這個引數會讓指令做不同的事。
引數同時是讀者唯一要動手改的位置。他把指令用在自己的機器上時,工具與子命令照抄、選項多半照抄,改的是引數。說明缺在這裡,指令就只剩照抄一種用法。
失效的形態
引數寫成字面值
adb -s <serial> shell screencap -p /sdcard/screen.png 裡,<serial> 被解釋過而 /sdcard/screen.png 沒有。兩者都是讀者要改的位置,差別只在長相:尖括號自帶「這裡要換」的訊號,字面值長得像一個完整可用的答案,於是它同時逃過作者的說明義務與讀者的警覺。
這一種最難發現,因為稿件上沒有缺口——那個位置填著一個看起來很正常的值。
引數的有無決定指令做什麼,而文章只示範不說明
同一篇文章用了 exec-out screencap -p > screen.png 與 shell screencap -p /sdcard/screen.png 兩種寫法。screencap 的檔案路徑引數是選用的,寫了就把 PNG 存成裝置上的檔、沒寫就把位元組送到標準輸出——兩種寫法的分歧全在這個引數上。文章沒說這件事時,讀者看得懂每一條,卻串不起為什麼一條用 > 另一條要 pull,也判斷不出自己該用哪一條。
補一段文字說明不足以修好它。 只給一條示範、另一條用散文描述時,讀者要從那段文字重建出另一條指令長什麼樣,而他來這一節就是為了拿指令——重建的工作正是示範本來要替他做掉的。兩條並列之後差別在版面上直接看得見,文字只需要說那個差別的意義。
多個引數的順序沒說
adb pull <裝置上的路徑> <本機的路徑> 的兩個引數不可對調。順序這件事在指令的外觀上沒有任何痕跡,而寫錯的後果是把本機的檔案推去覆蓋裝置上的。
整條指令零說明
一份指令清單裡混進一條從頭到尾沒有說明的(ls -l /storage/emulated/0/Android/data/<package>/)。清單的排版讓它看起來與鄰居同級,而鄰居各自有小節承接,它沒有。
選項解釋了而引數沒有
appops query-op SYSTEM_ALERT_WINDOW allow 旁邊的註解寫著「誰有這個權限」,那是這條指令的用途,不是兩個引數各填什麼。讀者知道它回答什麼問題,換一個 op 或換一個 mode 時仍不知道要動哪一個位置。
為什麼句子層的規則掃不到
管這件事的規則全部掛在句子上——四個槽位(動作者 / 動詞 / 受詞 / 結果)、主詞受詞完整、用詞精準。程式碼區塊不是句子,這幾條一條都不會被觸發:指令沒有主詞、沒有謂語,逐句判定的動作走到程式碼區塊就停下來,而區塊裡的每一個字面值就這樣通過。
同源審查也擋不住,理由與 #358 同構:審查者知道 /sdcard 是什麼,所以那條指令讀起來完整。缺的是說明,而審查者不需要那個說明。
判讀方式
一個問句,逐條指令可執行:
讀者要把這條指令用在自己的機器上,他得改哪幾個位置;每一個要改的位置,文章有沒有說過它填的是什麼。
答「不用改任何位置」的指令(adb devices、logcat -c)不適用這一條。答得出位置的,逐個回文章查有沒有對應的說明句。
同一個子命令在文章裡有兩種正當用法時另問一句:**兩種各自出現在程式碼區塊裡了嗎,還是其中一種只活在文字裡。**只活在文字裡的那一種要補成並列的示範。
掃描的入口是程式碼區塊裡的字面值,不是佔位符——佔位符多半已經被解釋過,字面值才是盲區。絕對路徑、常數名、op 名稱、namespace 名這幾類優先:
1rg -n '^\s*(adb|[a-z]+) .*\s/[a-z]' <檔案> # 指令裡的絕對路徑
2rg -n '^\s*adb .* [A-Z_]{4,}' <檔案> # 全大寫的常數引數這兩條抓的是已知形態、不是全集。引數也可能是小寫識別碼(secure、windows、allow)而那些沒有穩定的詞形,要靠逐條指令過上面那個問句。
修法
說明放在指令旁邊,不放在文章別處——讀者是為了用這條指令而停在這裡的。三種寫法按引數數量選:
- 一個引數:接在解釋這條指令的那一句裡(「
getprop讀裝置的系統屬性,引數是屬性名稱」) - 多個引數或多條相關指令:列成清單或表格,每條寫出引數的位置與意義
- 引數的有無改變行為:兩種形態各給一條指令,並列在同一個程式碼區塊裡,各自帶一行註解說它做什麼。並列是修法本身,文字說明是它的補充而不是替代——讀者選用哪一種的依據是兩條指令擺在一起的樣子。邊界:錯誤用法不進區塊(區塊是可以複製的位置),寫在散文裡用行內程式碼標出來就夠了
路徑類的引數另外交代選擇的理由。/sdcard 被選中是因為 pull 與 shell 讀得到的範圍不一樣,這個理由決定讀者換路徑時能換去哪裡。
跟其他抽象層原則的關係
- #358 技術文章的每一句寫清楚誰對什麼做了什麼事——同一個要求的兩個載體。那張管散文的句子,本卡管程式碼區塊;兩者的盲區成因相同(審查者自帶答案),而偵測動作不同,因為逐句判定進不了程式碼區塊。
- #342 寫出特定實例的值,就取代了讀者本來要做的查證——互補的一對。那張說不該寫的值寫了(讀者照抄一個關於別人機器的答案),本卡說該寫的值寫了而沒說它是什麼(讀者照抄一個他其實要換的位置)。兩張卡的判別問句共用同一個動作:讀者拿這一句去用,他是去查自己的系統,還是直接照抄。
- #113 商業邏輯論述要 self-contained——方向相反。那張處理散文依賴讀者去翻程式碼,本卡處理程式碼區塊依賴讀者自己補語意;兩者合起來的要求是兩種載體各自自足,不互相賒帳。
- #355 教學定位被讀成教室講課——本卡是那張的第三條規則(正確的做法與讀法直接寫在指令旁邊)在引數這個位置的具體形態。
Case
一篇 adb 遠端診斷的工作紀錄,十二個位置的引數沒有說明。最嚴重的是 screencap 的路徑引數:全篇同時使用給路徑與不給路徑兩種寫法,而那正是決定輸出去向的開關,從未被寫出來。這篇稿件走過完整的多輪審查、字句層逐類掃描與四槽位逐句判定,這一類一處都沒有被報出來——判定的單位是句子,而缺陷在程式碼區塊裡。
同一篇裡有三處做對了:驗證 pty 有沒有配置、驗證 stderr 有沒有混入、以及放寬設定與還原設定,三處都把兩條指令並列在同一個區塊。做對的三處與做錯的那一處差在讀者要不要選——前三處是「錯法對正確」或「設定對還原」,讀者照順序跑就好;screencap 那一處兩種都是正當用法而該用哪一種取決於環境,讀者要選,所以它最需要並列,卻是唯一只給一條的。先補文字說明那一版仍然不夠,是同一個理由:選擇的依據是兩條指令擺在一起的樣子。