Zsh 开发指南(第二十篇 代码风格)

导读

因为 shell 脚本语法比较灵活,写 shell 脚本的开发者熟悉的编程语言也有较大差异,大家很容易写出风格迥异的代码出来。如果只有自己一个人用还好,如果是大家合作开发同一个项目,代码风格不同就会造成不小的麻烦。所以约定一个代码风格是很有必要的。

本文中的代码风格约定只是我的个人建议,可以根据自己的需求或者喜欢来调整。本文的代码风格约定,在一定程度上也适用于 bash。

注意需要有丰富 shell 编程经验的人制定和维护代码风格约定,不然很容易无法执行或者流于形式而解决不了实际问题。代码风格约定不只需要约定代码怎么写,而且要说明为什么要这么写,不然容易因为难以服众而无法推广。

缩进

  • 统一使用 4 个空格来缩进。

原因:

  1. 要用空格而不是 tab。因为在终端上 cat less diff 等命令都将 tab 显示成 8 个空格,有些命令是不可配置的(即使可配置,要让所有机器配置同步也是件麻烦的事情)。如果自己在编辑器上配置 tab 为 4 个或者 2 个空格,那么就会和 cat less 等命令的显示方法不一致,会导致很多麻烦。
  2. 8 个空格太长,缩进几次就会导致行太长,而 shell 脚本每行不宜过长。
  3. 2 个空格的话,如果缩进比较频繁,看起来比较费劲。另外如果写代码时不小心多了或者少了一个空格,在某些场景,不看逻辑的话,就无法确定是多个一个还是少了一个,更容易导致他人错误的修改,或者代码越改越乱。
  4. 对于 4 个空格也可能导致缩进层数多时行太长的问题,通过修改逻辑减少缩进层数或者折行的方法,而不是减少缩进的空格数量来解决。

每行代码最多字符数

  • 非特殊场景,每行代码不超过 100 个字符。

原因:

  1. 代码过长,阅读起来不方便,用 diff 之类工具对代码进行分析处理也不方便,所以需要约定最长字符数。
  2. 经典的 80 个字符的约定,是受当时的输出设备限制而产生的标准,而现在的屏幕基本都是宽屏的,终端模拟器也都是可调大小的(而不是固定的 80x24)没必要削足适履迎合陈旧的标准,浪费屏幕空间。而且如果使用 80 个字符的约定,很容易遇到需要折行的情况,反而会导致可读性下降。
  3. 如果一行超过了 100 个字符,通常说明逻辑太多,需要分行或者折行。
  4. 某些特殊场景,比如显示一个 ASCII 字符组成的图片,会有一行超过 100 个字符的需求,所有不能严格执行每行必须不超过 100 个字符的约定。如果分行或者折行会不可避免地导致代码可读性下降,那么优先考虑可读性。

折行

  • 在前一行尾部加一个空格和 \ 折行,折行后缩进一层(4 个空格)。
  • 如果缩进的是一个文本块,可以使用对齐缩进,也可以使用 4 个空格的固定缩进。
  • 如果是在 aa && bb || cc[[ ]] 或者 (( )) 中折行,&& || 放在下一行的行首。

原因:

  1. 折行的缩进和普通的缩进都是为了体现代码的递进关系,没必要区分对待(比如折行缩进两层)。
  2. 如果为了看起来美观,使用对齐缩进而不是固定缩进。那么因为每个人的审美不同,很容易产生不同的缩进方法,从而产生不必要的麻烦。但对文本块来说比较特殊,因为通常对齐缩进不会产生争议。
  3. &&|| 在逻辑上属于后半个语句,在自然语言中也是这样,比如 明天我去公园或者去逛街,如果需要拆成两个子句,那么会是 明天我去公园,或者去逛街,而不是 明天我去公园或者,去逛街。对代码来说也是一样。而且把 &&|| 放在行首更容易对齐,看起来更舒服。

空格

  • 在缩进和对其之外的场景,不允许出现逻辑上不必要的连续多个空格。
  • + && | 等双元运算符左右要加一个空格。
  • ! ~等一元运算符和作用对象之间不加空格。
  • ( )(( )) { } 内侧不加空格,[[ ]] 因为语法需要,内侧加一个空格。
  • ; 之前不加空格,之后加一个空格。
  • 定义函数时(以及在 (( )) 中调用函数时),函数名和 ( 之间不加空格。
  • if while 等关键字和后边的内容之间加一个空格。
  • if [[ ]] { 等场景中,{ 和前边的内容之间加一个空格。
  • 变量和 [ ] 之间不加空格,用 [ ] 取数组或者哈希表值时,[ ] 内侧不加空格。
  • > < 等重定向符号和文件或者文件描述符之间不加空格。

原因:

  1. 适量地添加空格可以让代码更清晰易读。
  2. 这些约定基本属于很多编程语言代码风格中约定成俗的习惯,符合多数人的审美。

空行

  • 非特殊场景,不允许出现超过两个连续空行。
  • #!/bin/zsh 后加一个空行。
  • if while 等语句块之后加一个空行。
  • 定义函数后加一个空行。
  • 逻辑关系不强的两行(或者两块)代码之间,根据逻辑关系强弱(自行判断),加一个或两个空行。

原因:

  1. 适量添加空格,可以让代码逻辑按照空行分隔,提高可读性。
  2. 因为添加空行的方法涉及诸多因素,很难详细约定,主要靠开发者自行判断。

括号

  • 在判断条件的场景,不使用 [ ],用 [[ ]] 代替。
  • 在数值计算的场景,使用 $(( )) 而不是 $[ ]

原因:

  1. 在判断条件的场景,[ ] 的功能没有 [[ ]] 丰富,而且二者的用法存在差异,混合使用容易出问题。
  2. 在数值比较或者计算的场景,$[ ] 的功能没有 $(( )) 丰富,混合使用容易出问题。
  3. [ ] 在各种地方功能不一致,非必要场景尽量避免使用。

常量

  • 字符串常量中如果没有特殊符号,两端可以不加引号,也可以加引号。
  • 使用数值时,两端不加引号。

原因:

  1. 如果任何字符串常量两端都加引号,容易让代码中充斥着引号,影响可读性。并且如果不小心误删引号,容易导致难以定位错误。
  2. shell 脚本和很多其他编程语言不同,处理字符串的逻辑占很大部分,每个字符串常量两边都加引号的话,会增加很多额外工作量。

变量

  • $var 取变量值时,两边不加双引号,除非需要将非字符串变量转换成字符串。
  • 在非必须场景,不需要加 ${var} 中的大括号。
  • 能用局部变量的地方全部使用局部变量(用 local 定义)。
  • 变量名中的单词可以使用下划线分隔或者驼峰风格,在不影响可读性的情况也可以使用全小写字母,但在同一个文件中要一致。

原因:

  1. 和 bash 不同,zsh 在使用 $var 读取变量内容时,不用因为变量不存在、值为空、包含特殊符号而产生各种逻辑错误,所以无需在两端加双引号。
  2. $var 读变量是很多编程语言都有的用法,而 ${var} 几乎是 shell 中特有的用法,并且输入更麻烦,没必要推广这种用法。而且因为不加大括号导致变量名粘连而出错的情况,编写代码时即可识别出来,和外部输入无关,不需要为了避免不存在的问题而输入很多额外的大括号。
  3. 如果能使用局部变量的地方使用全局变量,更容易出现全局变量重名而互相影响导致错误的情况。这种错误是很难排查的(因为不会产生语法错误,容易让人怀疑是代码逻辑的问题,而不去检查是否有全局变量重名的情况),往往会浪费开发或者测试人员大量的时间。
  4. 不同编程语言的开发者对变量名的风格偏好不同,不宜规定统一风格。

引号

  • 字符串常量两端可以添加双引号或者单引号,但同一个文件中风格要一致。

原因:

  1. 双引号和单引号的功能不同,混合使用是不可避免的。
  2. 在双引号和单引号都适用的场景,统一使用一种引号,可以让代码更整洁易读。
  3. 编程语言背景不同的开发者,对单双引号的偏好不同,不宜强行规定默认使用的引号。

函数

  • 可以使用 name() 或者 function name() 定义函数,但同一个文件中风格要一致。

原因:

  1. 如果约定统一使用 name() 定义函数,那么没有照顾 JavaScript 等编程语言开发者的习惯,而且 function 关键字有助于代码的搜索。
  2. 如果约定统一使用 function name() 定义函数,需要额外输入 9 个字符,而意义有限,投入比产出要大。

脚本行数

  • 非特殊场景,单个脚本文件不超过 1000 行。

原因:

  1. 因为 shell 脚本的特性,单个脚本文件过长容易导致各种问题(比如全局变量互相影响)。1000 行代码对于多数场景都够用了。
  2. 如果写的是安装脚本之类需要分发的脚本,那么分发单个文件要比分发多个文件(需要打包解包等额外工作)容易很多,这种场景可能需要写长脚本。所以不宜强行规定单个脚本文件最大行数。

总结

本文介绍了我建议的 zsh 代码风格,可以适当参考。内容待完善。

本文不再更新,全系列文章在此更新维护:github.com/goreliu/zshguide

付费解决 Windows、Linux、Shell、C、C++、AHK、Python、JavaScript、Lua 等领域相关问题,灵活定价,欢迎咨询,微信 ly50247。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,839评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,543评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,116评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,371评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,384评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,111评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,416评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,053评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,558评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,007评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,117评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,756评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,324评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,315评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,539评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,578评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,877评论 2 345

推荐阅读更多精彩内容