Bash脚本学习笔记快速入门篇

脚本安全

我的所有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。

时间: 2025-01-25 01:03:32

Bash脚本学习笔记快速入门篇的相关文章

Nodejs学习笔记之入门篇_node.js

分享第一篇,关于 NodeJS -- Javascript 的常用知识以及如何从 Javascript 开发者过渡到 NodeJS 开发者(不会介绍具体的框架).在读本文前,希望你对 javascript 有一些初步的认识. Javascript 是一门原型模型的解释型语言.解释型将在后面的 NodeJS 里面讨论,原型链是 ES6 之前的 Javascript 的面向对象的实现方式之一,在 ES6 中支持的 class 增加了一种新的实现方式.在 Javascript 里面所有东西都是对象,包

Java中jqGrid 学习笔记整理——进阶篇(二)_java

相关阅读: Java中jqGrid 学习笔记整理--进阶篇(一) 本篇开始正式与后台(java语言)进行数据交互,使用的平台为 JDK:java 1.8.0_71 myEclisp 2015 Stable 2.0 Apache Tomcat-8.0.30 Mysql 5.7 Navicat for mysql 11.2.5(mysql数据库管理工具) 一.数据库部分 1.创建数据库 使用Navicat for mysql创建数据库(使用其他工具或直接使用命令行暂不介绍) 2. 2.创建表 双击打

jqGrid 学习笔记整理——进阶篇(一 )_jquery

 在浏览导航栏添加所需按钮 <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>DEMO</title> <link rel="stylesheet" type="text/css" href="css/jquery-ui.min.css" /> <link rel=

Javascript学习笔记之 对象篇(四) : for in 循环_基础知识

先上范例: // Poisoning Object.prototype Object.prototype.bar = 1; var foo = {moo: 2}; for(var i in foo) { console.log(i); // prints both bar and moo } 这里我们要注意两点,一是 for in 循环会忽略 enumerable 设置为 false 的属性.例如一个数组的 length 属性.第二是,由于 for in 会遍历整个原型链,所以当原型链过长时,会

Qt快速入门学习笔记(基础篇)

本文基于Qter开源社区论坛版主yafeilinux编写的<Qt快速入门系列教程目录>,网址:http://bbs.qter.org/forum.php?mod=viewthread&tid=193.参考书为基于该系列教程<Qt Creator快速入门>和<Qt及Qt Quick开发实战精解> 1.关联Qt库.如果是分别安装的Qt Creator和Qt库,而不是安装集成Qt Creator和Qt库的SDK,则需要手动关联Qt库.打开工具→选项菜单,然后选择&qu

php 快速入门篇一

我接触PHP也不是很久,所以有什么不足的地方,欢迎各位指正,让大家见笑了. 这篇小教程的对象是PHP初学者,都是些最简单.最基本的东西,因此高手们可以略过哦. 为了让各位初学者提起兴趣.尽快入门,这里写的是将是最简单,最基本的PHP程序,相信你只要有一点点的PHP基础知识,10分钟之内就能把它学会.没有PHP基础知识也没有关系,只要耐心的看,学会它也不会超过一个小时的. 我写这篇文章的目的是,和大家一起共同学习.共同进步,然后将PHP初学者们对PHP的恐惧心理驱赶到十万八千里之外,让大家增强自学

Struts学习傻瓜式入门篇

或许有人觉得struts不容易学,似乎里面的一些概念让未接触过的人迷惑,MVC1.MVC2.模式--我写这篇文章是想让从来没有接触过struts的人,能有个简单的入门指引,当然,系统地学习struts是必要的,里面有很多让人心醉的东东,那是后话了. 该案例包括首页,用户登陆.网站向导页面.就这么简单,没有深奥的struts概念,主要靠动手,然后用心体会. WEB Server用tomcat4.到 http://jakarta.apache.org 下载struts1.1,把zip文件释放到c:\

C#2.0泛型学习--入门篇

     最近频繁的接触到泛型方面的问题,所以也不得不来学习一下了,开始主要是在MSDN的WebCast上去下载教学录象看的(李建忠老师的),本篇文章先介绍一下泛型入门方面的知识,希望能让刚开始学习泛型的朋友能够更快的入门,言归正传,首先我们来看一下泛型的基本概念:      最显著的一点就是它参数化了类型,把类型作为参数抽象出来,从而使我们在实际的运用当中能够更好的实现代码的重复利用,同时它提供了更强的类型安全,更高的效率,不过在约束方面,它只支持显示的约束,这样在灵活性方面就显得不是那么好了

C#学习笔记(一)-- 入门的困惑

笔记 简单的我就不写了,主要写一下C#学习中的要点和难点.1.由HelloWorld开始先看一段基本上每本C#书里都会讲到的例子,很老土.using System;namespace test{     class Class1     {         [STAThread]         static void Main(string[] args)         {              System.Console.WriteLine("Hello,World!");