查看原文
其他

Shell | 大厂开发都在使用的Shell技巧

上海小胖 Python专栏 2018-10-26




不甘现状的上海土著                                                                        插画师 /小胖




编辑 /小胖


今天的技术技巧最初是来自谷歌的“Testing on the Toilet” (TOTT)。

这只是一个修订和扩增版本。


脚本安全

我的所有bash脚本都以下面几句为开场白:

#!/bin/bash
set -o nounset
set -o errexit

这样做会避免两种常见的问题:

  • 引用未定义的变量(缺省值为“”)

  • 执行失败的命令被忽略

需要注意的是,有些Linux命令的某些参数可以强制忽略发生的错误,例如“mkdir -p” 和 “rm -f”。

还要注意的是,在“errexit”模式下,虽然能有效的捕捉错误,但并不能捕捉全部失败的命令,在某些情况下,一些失败的命令是无法检测到的。


脚本函数

在bash里你可以定义函数,它们就跟其它命令一样,可以随意的使用;它们能让你的脚本更具可读性:

ExtractBashComments() {
  egrep "^#"
}

cat myscript.sh | ExtractBashComments | wc
comments=$(ExtractBashComments < myscript.sh)

还有一些例子:

SumLines() {  
# 一直迭代输入,类似awkawk      
  local sum=0
  local line=””
  while read line ; do
      sum=$((${sum} + ${line}))
  done
  echo ${sum}
}

SumLines < data_one_number_per_line.txt

log() {  
# 一个典型的日志分析
 local prefix="[$(date +%Y/%m/%d\ %H:%M:%S)]: "
 echo "${prefix} $@" >&2
}

log "INFO" "a message"

尽可能的把你的bash代码移入到函数里,仅把全局变量、常量和对“main”调用的语句放在最外层。


变量注解

Bash里可以对变量进行有限的注解。最重要的两个注解是:

  • local(函数内部变量)

  • readonly(只读变量)

# DEFAULT_VAL 默认值可以被同名的环境变量给重写
readonly DEFAULT_VAL=${DEFAULT_VAL:-7}

myfunc() {
# 以一个全局变量来初始化一个本地变量
 local some_var=${DEFAULT_VAL}
 ...
}

这样,你可以将一个以前不是只读变量的变量声明成只读变量:

x=5
x=6
readonly x
x=7  
# 执行失败

尽量对你bash脚本里的所有变量使用local或readonly进行注解。


用$()代替反单引号(`)

反单引号很难看,在有些字体里跟正单引号很相似。

$()能够内嵌使用,而且避免了转义符的麻烦。

# both commands below print out: A-B-C-D
echo "A-`echo B-\`echo C-\\\`echo D\\\`\``"
echo "A-$(echo B-$(echo C-$(echo D)))"


用[[]](双层中括号)替代[]

使用[[]]能避免像异常的文件扩展名之类的问题,而且能带来很多语法上的改进,而且还增加了很多新功能

操作符    功能说明

||         逻辑or(仅双中括号里使用)

&&         逻辑and(仅双中括号里使用)

<         字符串比较(双中括号里不需要转移)

-lt         数字比较

=         字符串相等

==         以Globbing方式进行字符串比较(仅双中括号里使用,参考下文)

=~         用正则表达式进行字符串比较(仅双中括号里使用,参考下文)

-n         非空字符串

-z         空字符串

-eq         数字相等

-ne         数字不等


单中括号:

[ "${name}" \> "a" -o ${name} \< "m" ]

双中括号

[[ "${name}" > "a" && "${name}" < "m"  ]]


正则表达式/Globbing

使用双中括号带来的好处用下面几个例子最能表现:

t="abc123"

[[ "$t" == abc* ]]        
# true (globbing比较)

[[ "$t" == "abc*" ]]      
# false (字面比较)

[[ "$t" =~ [abc]+[123]+ ]]
# true (正则表达式比较)

[[ "$t" =~ "abc*" ]]      
# false (字面比较)

注意,从bash 3.2版开始,正则表达式和globbing表达式都不能用引号包裹。


内置变量

变量        说明

$0         脚本名称

$n         传给脚本/函数的第n个参数

$$         脚本的PID

$!         上一个被执行的命令的PID(后台运行的进程)

$?         上一个命令的退出状态(管道命令使用${PIPESTATUS})

$#         传递给脚本/函数的参数个数

$@         传递给脚本/函数的所有参数(识别每个参数)

$*         传递给脚本/函数的所有参数(把所有参数当成一个字符串)



小胖开设了「Python专栏」星球,里面会有「Python原创」、「大航海计划」、「问题解答」、「面试刷题」、「大厂内推」、「技术分享」等。

目前正在做的是一个「大航海计划」:由船长发起,分为多个阶段,或招募或淘汰若干名船员,每周都会在星球做一次项目汇报。启动资金由船员众筹10元,与我无关。目标是让球友能够在星球里找到志同道合的小伙伴一起驶向新世界。

加入星球,每天0.18元,在这个星球能够得到的,不只是关于Python,圈子、人脉、资源,学习氛围,眼界都是比技术更值得去借鉴的东西。

也欢迎球友提问,每天我都会回答大家的问题,方向不限。


推荐阅读:

Python | 用Python监听邻居家小姐姐的上网行为

Python | 提升Python程序性能的7个习惯,你都有吗?


    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存