这八个建议,来源于键者几年来编写 shell 脚本的一些经验和教训。事实上开始写的时候还不止这几条,后来思索再三,去掉几条无关痛痒的,最后剩下八条。毫不夸张地说,每条都是精挑细选的,虽然有几点算是老生常谈了。

1

指定bash

shell 脚本的第一行,#!之后应该是什么?

如果拿这个问题去问别人,不同的人的回答可能各不相同。我见过/usr/bin/env bash,也见过/bin/bash,还有/usr/bin/bash,还有/bin/sh,还有/usr/bin/env sh。这算是编程界的“’茴’字四种写法”了。

在多数情况下,以上五种写法都是等价的。但是,写过程序的人都知道:“少数情况”里往往隐藏着意想不到的坑。

如果系统的默认 shell 不是 bash 怎么办?比如某 Linux 发行版的某个版本,默认的 sh 就不是 bash。

如果系统的 bash 不是在 /usr/bin/bash 怎么办?

我推荐使用 /usr/bin/env bash 和 /bin/bash。前者通过env添加一个中间层,让env在$PATH中搜索bash;后者则是官方背书的,约定俗成的 bash 位置,/usr/bin/bash不过是指向它的一个符号链接。

2

set -e 和 set -x

OK,经过一番讨论,现在第一行定下来了。接下来该开始写第二行了吧?

且慢!在你开始构思并写下具体的代码逻辑之前,先插入一行set -e和一行set -x。

set -x会在执行每一行 shell 脚本时,把执行的内容输出来。它可以让你看到当前执行的情况,里面涉及的变量也会被替换成实际的值。

set -e会在执行出错时结束程序,就像其他语言中的“抛出异常”一样。(准确说,不是所有出错的时候都会结束程序,见下面的注)

注:set -e结束程序的条件比较复杂,在man bash里面,足足用了一段话描述各种情景。大多数执行都会在出错时退出,除非 shell 命令位于以下情况:

一个 pipeline 的非结尾部分,比如 error | ok

一个组合语句的非结尾部分,比如 ok && error || other

一连串语句的非结尾部分,比如 error; ok

位于判断语句内,包括 test、if、 while 等等。

这两个组合在一起用,可以在 debug 的时候替你节省许多时间。出于防御性编程的考虑,有必要在写第一行具体的代码之前就插入它们。扪心自问,写代码的时候能够一次写对的次数有多少?大多数代码,在提交之前,通常都经历过反复调试修改的过程。与其在焦头烂额之际才引入这两个配置,不如一开始就给 debug 留下余地。在代码终于可以提交之后,再考虑是否保留它们也不迟。

3

带上shellcheck

好了,现在我已经有了三行(样板)代码,具体的业务逻辑一行都没写呢。是不是该开始写了?

且慢!工欲善其事,必先利其器。这次,我就介绍一个 shell 脚本编写神器:shellcheck

说来惭愧,虽然写了几年 shell 脚本,有些语法我还是记不清楚。这时候就要依仗 shellcheck 指点一下了。

shellcheck 除了可以提醒语法问题以外,还能检查出 shell 脚本编写常见的 bad code。本来我的N条建议里面,还有几条是关于这些 bad code 的,不过考虑到 shellcheck 完全可以发掘出这些问题,于是忍痛把它们都剔除在外了。毫无疑问,使用 shellcheck 给我的 shell 编写技能带来了巨大的飞跃。

所谓“站在巨人的肩膀上”,虽然我们这些新兵蛋子,技能不如老兵们强,但是我们可以在装备上赶上对方啊!动动手安装一下,就能结识一个循循善诱的“老师”,何乐而不为?

顺便一提,shellcheck 居然是用 haskell 写的。谁说 haskell 只能用来装逼?

4

变量展开

在 shell 脚本中,偶尔可以看到这样的做法:echo $xxx | awk/sed/grep/cut...。看起来大张形势的样子,其实不过是想修改一个变量的值。

杀鸡何必用牛刀?bash 内建的变量展开机制已经足以满足你各种需求!还是老方法, read the f**k manaul! man bash 然后搜索Parameter Expansion,下面就是你想要的技巧。键者也写过一篇相关的文章,希望能助上一臂之力:玩转 Bash变量。

5

注意local

随着代码越写越多,你开始把重复的逻辑提炼成函数。有可能你会掉到 bash 的一个坑里。在 bash,如果不加 local 限定词,变量默认都是全局的。变量默认全局——这跟 js 和 lua 相似;但相较而言,很少有 bash 教程一开始就告知你这个事实。在顶级作用域里,是否是全局变量并不重要。但是在函数里面,声明一个全局变量可能会污染到其他作用域(尤其在你根本没有注意到这一点的情况下)。所以,对于在函数内声明的变量,请务必记得加上 local 限定词。

6

trap 信号

如果你写过稍微复杂点的在后台运行的程序,应该知道 posix 标准里面“信号”是什么一回事。如果不知道,直接看下一段。像其他语言一样,shell 也支持处理信号。trap sighandler INT可以在接收到 SIGINT 时调用 sighandler 函数。捕获其他信号的方式以此类推。

不过 trap 的主要应用场景可不是捕获哪个信号。trap 命令支持“捕获”许多不同的流程——准确来说,允许用户给特定的流程注入函数调用。其中最为常用的是trap func EXIT和trap func ERR。

trap func EXIT允许在脚本结束时调用函数。由于无论正常退出抑或异常退出,所注册的函数都能得以调用,在需要调用一个清理函数的场景下,我都是用它注册清理函数,而不是简单地在脚本结尾调用清理函数。

trap func ERR允许在运行出错时调用函数。一个常用的技法是,使用全局变量ERROR存储错误信息,然后在注册的函数中根据存储的值完成对应的错误报告。把原本四分五裂的错误处理逻辑集中到一处,有时候会起奇效。不过要记住,程序异常退出时,既会调用EXIT注册的函数,也会调用ERR注册的函数。

7

三思后行

以上几条都是具体的建议,剩下两条比较务虚。

这条建议的名字叫“三思而行”。其实无论写什么代码,哪怕只是一个辅助脚本,都要三思而行,切忌粗心大意。不,写脚本的时候更要记住这点。毕竟许多时候,一个复杂的脚本发端于几行小小的命令。一开始写这个脚本的人,也许以为它只是一次性任务。代码里难免对一些外部条件有些假定,在当时也许是正常的,但是随着外部环境的变化,这些就成了隐藏的暗礁。雪上加霜的是,几乎没有人会给脚本做测试。除非你去运行它,否则不知道它是否还能正常使用。

要想减缓脚本代码的腐烂速度,需要在编写的时候辨清哪些是会变的依赖、哪些是脚本正常运行所不可或缺的。要有适当的抽象,编写可变更的代码;同时要有防御性编程的意识,给自己的代码一道护城河。

8

扬长避短

有些时候,使用 shell 写脚本就意味着难以移植、难以统一地进行错误处理、难以利索地处理数据。

虽然使用外部的命令可以方便快捷地实现各种复杂的功能,但作为硬币的反面,不得不依靠grep、sed、awk等各种工具把它们粘合在一起。

如果有兼容多平台的需求,还得小心规避诸如BSD和GNU coreutils,bash版本差异之类奇奇怪怪的陷阱。

由于缺乏完善的数据结构以及一致的API,shell 脚本在处理复杂的逻辑上力不从心。

解决特定的问题要用合适的工具。知道什么时候用 shell,什么时候切换到另外一门更通用的脚本语言(比如ruby/python/perl),这也是编写可靠 shell 脚本的诀窍。如果你的任务可以组合常见的命令来完成,而且只涉及简单的数据,那么 shell 脚本就是适合的锤子。如果你的任务包含较为复杂的逻辑,而且数据结构复杂,那么你需要用 ruby/python 之类的语言编写脚本。

3月云计算在线免费训练营开讲啦5天技术免费学扫码预约抢名额

如有疑问,速寻达妹微信:zdzc3087880280;达妹QQ:3535503962。

精彩推荐:

  • 达内教育面向全国用户提供免费课程!(文中申请)

  • 都在"云"上,你说云运维会吃香不?

  • 运维的新“加码”技能—Python!

  • 2019年达内学员入职中国软件百强榜!完整榜单!

点击“

shell grep 变量_老司机给出的关于 shell 脚本的8个建议,必收!相关推荐

  1. bash shell sed 获取第一行_老司机给出的关于 shell 脚本的8个建议,必收!

    这八个建议,来源于键者几年来编写 shell 脚本的一些经验和教训.事实上开始写的时候还不止这几条,后来思索再三,去掉几条无关痛痒的,最后剩下八条.毫不夸张地说,每条都是精挑细选的,虽然有几点算是老生 ...

  2. 汽车故障检测软件_汽车没故障,定期保养,为啥尾气检测通不过?老司机说出关键...

    根据相关规定,普通私家轿车前6年不用上线检测,只需要每隔两年到车管所申请年检合格标志就可以,但是6年以后要每年定期进行汽车年检,如果没有通过汽车年检,恐怕会强制报废.汽车没故障,定期保养,为啥尾气检测 ...

  3. dnf游戏币比例计算机怎么算,DNF:游戏币比例降低到1比35,究竟是好还是坏?老司机说出了心声...

    原标题:DNF:游戏币比例降低到1比35,究竟是好还是坏?老司机说出了心声 自90版本末期开始,TX就开始挥着屠刀冲向多开工作室,先是改变地图收益模式,后是大力制裁多开同步,经过长达半年的打击,在95 ...

  4. shell grep 变量_Shell应用:巧用xargs 轻松实现上万文件的筛选压缩

    背景 Linux 下某个目录下有小文件好几万个,命名规则按日期每小时 N 个,想要过滤出指定日期的文件并添加到压缩文件中,记录下脚本化的过程如下. 正则过滤存在的问题 首先,直接进入该目录,由于文件数 ...

  5. 老司机写的java代码_老司机告诉你高质量的Java代码是怎么练成的?

    一提起程序员,首先想到的一定是"码农",对,我们是高产量的优质"码农",我们拥有超跃常人的逻辑思维以及不走寻常路的分析.判别能力,当然,我们也有良好的编码规范, ...

  6. c++ 箭头符号怎么打_老司机怎么测量三极管管型、管脚和性能?三极管测量此中有深意...

    众所周知,三极管具有基极.集电极.发射极三极,另外还有NPN型还有PNP型,那么如何用最快的方法进行三极管测量来确认三极管的管脚和管型.另外,三极管的工作状态是否可以通过三极管测量进行判断?接下来本网 ...

  7. 鸿蒙汽修大明路店,为啥很多车主在4S店首保之后都会选择汽修厂保养?老司机说出原因...

    虽然说现在汽车的价格越来越便宜了,但是一辆普通的汽车少说也要十万块,现在的消费水平如此高,很多车主都抱怨到能买得起车却开不起,汽车买回来到处都要花钱,上路之前要买保险,上路之后要加油,另外还需要保养等 ...

  8. python打开交互界面设计_老司机必备——用PyQt做一个有交互界面的妹子图爬虫...

    内容简介:老司机必备--用PyQt做一个有交互界面的妹子图爬虫 代码飙车,指日可待.今天的课程教大家结合PyQt和 Python 爬虫技术,做一个带有交互界面的妹子图网站爬虫程序. 一.实验简介 1. ...

  9. 扫地机器人能过凸起地面吗_老司机教你一眼看穿扫地机器人的套路

    原标题:老司机教你一眼看穿扫地机器人的套路 说到扫地机器人,想必大家都不陌生.它虽然很早就出现了,但确是近两年才走入人们视线的.敢于吃螃蟹的人有的已经体会到了扫地机器人带给我们的便利,有的用了几次感觉 ...

最新文章

  1. 东田纳西州立大学计算机排名,2019东田纳西州立大学世界排名
  2. 比如“古今数学思想”,BBC的“数学的故事”视频,一起看效果会更好
  3. linux 查看其他磁盘分区,Linux 查看磁盘分区.pdf
  4. 更改配置:远程访问gitlab的postgresql数据库
  5. mysql abc_MySql ABC
  6. html u方法,success 方法
  7. 软件开发工程师--经验之谈
  8. 微信支付开发(3) 对账单
  9. linux中级之HAProxy基础配置
  10. 【协同任务】基于matlab多无人机目标搜索与围捕【含Matlab源码 1205期】
  11. matlab7.0窗口教程,MATLAB7.0实用教程
  12. 威联通建php邮件服务器_威联通 ※ 群晖 虚拟机性能对比 我可能要碰瓷 eSir
  13. Resource temporarily unavailable
  14. markdown生成目录
  15. 数据类型,生命周期作用域
  16. php数字转换题目,PHP将整数数字转换为罗马数字实例分享
  17. html铺满整个页面_html 怎么让背景图铺满整个页面?
  18. 抖音商家找达人带货怎么给链接?抖音带货操作方法分享
  19. js 各省市地名数据(包含各省市区域代码)(未测试)
  20. 老字号品牌营销杂志老字号品牌营销杂志社老字号品牌营销编辑部2022年第23期目录

热门文章

  1. C#中await/async闲说
  2. surging 微服务引擎 1.0 正式发布
  3. ASP.NET Core 中的中间件
  4. 使用C# (.NET Core) 实现命令设计模式 (Command Pattern)
  5. 初探SQL Server 2017 on Docker@macOS
  6. 编写高性能 .NET 代码 第二章:垃圾回收 基本操作
  7. 从真实项目中抠出来的设计模式——第二篇:过滤器模式
  8. Python3.6学习笔记(三)
  9. C#趣味程序---水仙花数
  10. C语言试题九十二之输入数据有多行,第一行是一个整数n,表示测试用例的个数,后面跟着n行 ,每行包括一个由字母和数字组成的字符串