Bash脚本15分钟进阶指导
首先声明这是网上摘来的。这里的技术技巧最初是来自谷歌的“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() { # iterating over stdin - similar to awk
local sum=0
local line=””
while read line ; do
sum=$((${sum} + ${line}))
done
echo ${sum}
}
SumLines < data_one_number_per_line.txt
log() { # classic logger
local prefix="[$(date +%Y/%m/%d\ %H:%M:%S)]: "
echo "${prefix} $@" >&2
}
log "INFO" "a message"
尽可能的把你的bash代码移入到函数里,仅把全局变量、常量和对“main”调用的语句放在最外层。
变量注解
Bash里可以对变量进行有限的注解。最重要的两个注解是:
local(函数内部变量)readonly(只读变量)
# a useful idiom: DEFAULT_VAL can be overwritten
# with an environment variable of the same name
readonly DEFAULT_VAL=${DEFAULT_VAL:-7}
myfunc() {
# initialize a local variable with the global default
local some_var=${DEFAULT_VAL}
...
}
这样,你可以将一个以前不是只读变量的变量声明成只读变量:
x=5
x=6
readonly x
x=7 # failure
尽量对你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表达式都不能用引号包裹。如果你的表达式里有空格,你可以把它存储到一个变量里:
r="a b+"
[[ "a bbb" =~ $r ]] # true
按Globbing方式的字符串比较也可以用到case语句中:
case $t in
abc*) <action> ;;
esac
字符串操作
Bash里有各种各样操作字符串的方式,很多都是不可取的。
基本用户
f="path1/path2/file.ext"
len="${#f}" # = 20 (字符串长度)
# 切片操作: ${<var>:<start>} or ${<var>:<start>:<length>}
slice1="${f:6}" # = "path2/file.ext"
slice2="${f:6:5}" # = "path2"
slice3="${f: -8}" # = "file.ext"(注意:"-"前有空格)
pos=6
len=5
slice4="${f:${pos}:${len}}" # = "path2"
替换操作(使用globbing)
f="path1/path2/file.ext"
single_subst="${f/path?/x}" # = "x/path2/file.ext"
global_subst="${f//path?/x}" # = "x/x/file.ext"
# 字符串拆分
readonly DIR_SEP="/"
array=(${f//${DIR_SEP}/ })
second_dir="${arrray[1]}" # = path2
删除头部或尾部(使用globbing)
f="path1/path2/file.ext"
# 删除字符串头部
extension="${f#*.}" # = "ext"
# 以贪婪匹配方式删除字符串头部
filename="${f##*/}" # = "file.ext"
# 删除字符串尾部
dirname="${f%/*}" # = "path1/path2"
# 以贪婪匹配方式删除字符串尾部
root="${f%%/*}" # = "path1"
避免使用临时文件
有些命令需要以文件名为参数,这样一来就不能使用管道。这个时候 <() 就显出用处了,它可以接受一个命令,并把它转换成可以当成文件名之类的什么东西:
# 下载并比较两个网页
diff <(wget -O - url1) <(wget -O - url2)
还有一个非常有用处的是”here documents”,它能让你在标准输入上输入多行字符串。下面的’MARKER’可以替换成任何字词。
# 任何字词都可以当作分界符
command << MARKER
...
${var}
$(cmd)
...
MARKER
如果文本里没有内嵌变量替换操作,你可以把第一个MARKER用单引号包起来:
command << 'MARKER'
...
no substitution is happening here.
$ (dollar sign) is passed through verbatim.
...
MARKER
内置变量
| 变量 | 说明 |
|---|---|
| $0 | 脚本名称 |
| $n | 传给脚本/函数的第n个参数 |
| $$ | 脚本的PID |
| $! | 上一个被执行的命令的PID(后台运行的进程) |
| $? | 上一个命令的退出状态(管道命令使用${PIPESTATUS}) |
| $# | 传递给脚本/函数的参数个数 |
| $@ | 传递给脚本/函数的所有参数(识别每个参数) |
| $* | 传递给脚本/函数的所有参数(把所有参数当成一个字符串) |
$*很少是正确的选择。$@能够处理空格参数,而且参数间的空格也能正确的处理。$@时应该用双引号括起来,像”$@”这样。调试
对脚本进行语法检查:
bash -n myscript.sh
跟踪脚本里每个命令的执行:
bash -v myscripts.sh
跟踪脚本里每个命令的执行并附加扩充信息:
bash -x myscript.sh
你可以在脚本头部使用set -o verbose和set -o xtrace来永久指定-v和-o。当在远程机器上执行脚本时,这样做非常有用,用它来输出远程信息。
什么时候不应该使用bash脚本
- 你的脚本太长,多达几百行
- 你需要比数组更复杂的数据结构
- 出现了复杂的转义问题
- 有太多的字符串操作
- 不太需要调用其它程序和跟其它程序管道交互
- 担心性能
这个时候,你应该考虑一种脚本语言,比如Python或Ruby。
Bash脚本15分钟进阶指导的更多相关文章
- Bash脚本15分钟进阶教程
转载: Bash脚本15分钟进阶教程 这里的技术技巧最初是来自谷歌的"Testing on the Toilet" (TOTT).这里是一个修订和扩增版本. 脚本安全 我的所有ba ...
- Bash 脚本进阶,经典用法及其案例
前言:在linux中,Bash脚本是很基础的知识,大家可能一听脚本感觉很高大上,像小编当初刚开始学一样,感觉会写脚本的都是大神.虽然复杂的脚本是很烧脑,但是,当我们熟练的掌握了其中的用法与技巧,再多加 ...
- Bash脚本编写初体验
上周例会的时候,冷不丁的接到了维护原有的安装脚本和编写升级.卸载脚本的任务,PM和几个同事一本正经的说,一天甚至30分钟就可以精通shell脚本编写,哪怕没有语言基础也可以. 当然,作为有着C++.P ...
- 15分钟在笔记本上搭建 Kubernetes + Istio开发环境
11月13~15日,KubeCon 上海大会召开,云原生是这个秋天最火热的技术.很多同学来问如何上手 Kubernetes和Istio 服务网格开发.本文将帮助你利用Docker CE桌面版,15分钟 ...
- 生产力工具:shell 与 Bash 脚本
生产力工具:shell 与 Bash 脚本 作者:吴甜甜 个人博客网站: wutiantian.github.io 注意:本文只是我个人总结的学习笔记,不适合0基础人士观看. 参考内容: 王顶老师 l ...
- 15分钟从零开始搭建支持10w+用户的生产环境(三)
上一篇文章介绍了这个架构中,选择MongoDB做为数据库的原因,及相关的安装操作. 原文地址:15分钟从零开始搭建支持10w+用户的生产环境(二) 三.WebServer 在SOA和gRPC大行其 ...
- 15分钟学会使用Git和远程代码库
git是个了不起但却复杂的源代码管理系统.它能支持复杂的任务,却因此经常被认为太过复杂而不适用于简单的日常工作.让我们诚实一记吧:Git是复杂的,我们不要装作它不是.但我仍然会试图教会你用(我的)基本 ...
- 【转】Bash脚本实现批量作业并行化
首先附上自己常用的代码 ---------------------------------------------------------------------------------------- ...
- Bash脚本编程基础
为实现某个任务,将许多命令组合后,写入一个可执行的文本文件的方法,称为Shell脚本编程. 按照应用的Shell环境不同,可以将Shell脚本分为多种类型.其中最常见的是应用于Bash和Tcsh的脚本 ...
随机推荐
- Hessian最佳实践
前言:本文主要介绍‘独立的Hessian技术’与‘结合Spring技术’的两种Hessian接口开发模式及代码示例. 一.独立的Hessian技术开发步骤 Hessian-Java服务器端必须具备以下 ...
- kali-dmitry 域名信息收集
DMirty:用来收集以下信息: 1. 端口扫描 2. whois主机IP和域名信息 3. 从Netcraft.com获取主机信息 4. 子域名 5. 域名中包含的邮件地址 1.使用方法: Deepm ...
- Windows 2008 R2 强制删除Cluster
在正常删除Cluster 节点之后,再添加节点时,报“节点已经加入群集”,无法加入,注册表信息删除后可正常移除Cluster服务,如下: HKEY_LOCAL_MACHINE\SYSTEM\Curre ...
- WPF中的image控件的Source赋值
WPF中的Image控件Source的设置 1.XAML中 简单的方式(Source="haha.png"); image控件的Source设置为相对路径后(Source=&quo ...
- 基于Bootstrap、Jquery的自适应导航栏
css代码: .kch_nav{width: 100%; position: fixed;z-index: 999;top:0;left: 0;right: 0;height: 69px;backgr ...
- sqlite里执行查询提示未启用约束、主键冲突之——数据竟能超字段长度存储
数据表设计如图:szflbm为主键 数据表主键数据: 以上数据在查询时,执行到该语句adapter.Fill(table); 提示主键冲突. 解决: 1.尝试修改数据,把ZC1改成ZZ,正常.说明原因 ...
- Elasticsearch mysql 增量同步 三表联合 脚本
在上一篇中简略的说了一下es同步数据脚本的大致情况,但是实际情况里肯定不会像上一篇里面的脚本那么简单.比如目前我就有三张表,两张实体表,一张关联表.大致实现如下: bin目录建立一个statefile ...
- NSOperationQueue与GCD的使用原则和场景
首先,我们要明确NSOperationQueue与GCD之间的关系: NSOpertaionQueue用GCD构建封装的,是GCD的高级抽象. 其次,我们要区别两者的不同: GCD仅仅支持FIFO队列 ...
- java安全沙箱(二)之.class文件检验器
java是一种类型安全的语言,它有四类称为安全沙箱机制的安全机制来保证语言的安全性,这四类安全沙箱分别是: 类加载体系 .class文件检验器 内置于Java虚拟机(及语言)的安全特性 安全管理器及J ...
- .NET4.5 异步编程 async和await
msdn介绍:https://msdn.microsoft.com/zh-cn/library/hh191443.aspx 其实很简单,标记了async的方法为异步方法,从方法的左大括号开始同步执行, ...