什么是Shell脚本

示例

看个例子吧:

#!/bin/sh
cd ~
mkdir shell_tut
cd shell_tut for ((i=0; i<10; i++)); do
touch test_$i.txt
done

示例解释

  • 第1行:指定脚本解释器,这里是用/bin/sh做解释器的
  • 第2行:切换到当前用户的home目录
  • 第3行:创建一个目录shell_tut
  • 第4行:切换到shell_tut目录
  • 第5行:循环条件,一共循环10次
  • 第6行:创建一个test_1…10.txt文件
  • 第7行:循环体结束

cd, mkdir, touch都是系统自带的程序,一般在/bin或者/usr/bin目录下。for, do, done是sh脚本语言的关键字。

shell和shell脚本的概念

shell是指一种应用程序,这个应用程序提供了一个界面,用户通过这个界面访问操作系统内核的服务。Ken Thompson的sh是第一种Unix Shell,Windows Explorer是一个典型的图形界面Shell。

shell脚本(shell script),是一种为shell编写的脚本程序。业界所说的shell通常都是指shell脚本,但读者朋友要知道,shell和shell script是两个不同的概念。由于习惯的原因,简洁起见,本文出现的“shell编程”都是指shell脚本编程,不是指开发shell自身(如Windows Explorer扩展开发)。

环境

shell编程跟java、php编程一样,只要有一个能编写代码的文本编辑器和一个能解释执行的脚本解释器就可以了。

OS

当前主流的操作系统都支持shell编程,本文档所述的shell编程是指Linux下的shell,讲的基本都是POSIX标准下的功能,所以,也适用于Unix及BSD(如Mac OS)。

Linux

Linux默认安装就带了shell解释器。

Mac OS

Mac OS不仅带了sh、bash这两个最基础的解释器,还内置了ksh、csh、zsh等不常用的解释器。

Windows上的模拟器

windows出厂时没有内置shell解释器,需要自行安装,为了同时能用grep, awk, curl等工具,最好装一个cygwin或者mingw来模拟linux环境。

脚本解释器

sh

即Bourne shell,POSIX(Portable Operating System Interface)标准的shell解释器,它的二进制文件路径通常是/bin/sh,由Bell Labs开发。

本文讲的是sh,如果你使用其它语言用作shell编程,请自行参考相应语言的文档。

bash

Bash是Bourne shell的替代品,属GNU Project,二进制文件路径通常是/bin/bash。业界通常混用bash、sh、和shell,比如你会经常在招聘运维工程师的文案中见到:熟悉Linux Bash编程,精通Shell编程。

在CentOS里,/bin/sh是一个指向/bin/bash的符号链接:

[allen@ctos-001 ~]$ ls -l /bin/*sh
-rwxr-xr-x. 1 root root 938768 2月 22 2013 /bin/bash
-rwxr-xr-x. 1 root root 11239 3月 25 10:16 /bin/clbash
lrwxrwxrwx. 1 root root 4 1月 6 11:07 /bin/csh -> tcsh
-rwxr-xr-x. 1 root root 109672 10月 17 2012 /bin/dash
lrwxrwxrwx. 1 root root 4 1月 6 11:02 /bin/sh -> bash
-rwxr-xr-x. 1 root root 387328 2月 22 2013 /bin/tcsh

但在Mac OS上不是,/bin/sh和/bin/bash是两个不同的文件:

MacBook:~ allenyang$ ls -l /bin/*sh
-r-xr-xr-x 1 root wheel 628496 10 18 2015 /bin/bash
-rwxr-xr-x 1 root wheel 378624 10 18 2015 /bin/csh
-r-xr-xr-x 1 root wheel 1394432 10 18 2015 /bin/ksh
-r-xr-xr-x 1 root wheel 632672 10 18 2015 /bin/sh
-rwxr-xr-x 1 root wheel 378624 10 18 2015 /bin/tcsh
-rwxr-xr-x 1 root wheel 573600 10 18 2015 /bin/zsh

高级编程语言

理论上讲,只要一门语言提供了解释器(而不仅是编译器),这门语言就可以胜任脚本编程,常见的解释型语言都是可以用作脚本编程的,如:Perl、Tcl、Python、PHP、Ruby。Perl是最老牌的脚本编程语言了,Python这些年也成了一些linux发行版的预置解释器。

编译型语言,只要有解释器,也可以用作脚本编程,如C shell是内置的(/bin/csh),Java有第三方解释器Jshell,Ada有收费的解释器AdaScript。

如下是一个PHP Shell Script示例(假设文件名叫test.php):

#!/usr/bin/php
<?php
for ($i=0; $i < 10; $i++)
echo $i . "\n";

执行:

/usr/bin/php test.php

或者:

chmod +x test.php
./test.php

如何选择shell编程语言

熟悉 vs 陌生

如果你已经掌握了一门编程语言(如PHP、Python、Java、JavaScript),建议你就直接使用这门语言编写脚本程序,虽然某些地方会有点啰嗦,但你能利用在这门语言领域里的经验(单元测试、单步调试、IDE、第三方类库)。

新增的学习成本很小,只要学会怎么使用shell解释器(Jshell、AdaScript)就可以了。

简单 vs 高级

如果你觉得自己熟悉的语言(如Java、C)写shell脚本实在太啰嗦,你只是想做一些备份文件、安装软件、下载数据之类的事情,学着使用sh,bash会是一个好主意。

shell只定义了一个非常简单的编程语言,所以,如果你的脚本程序复杂度较高,或者要操作的数据结构比较复杂,那么还是应该使用Python、Perl这样的脚本语言,或者是你本来就已经很擅长的高级语言。因为sh和bash在这方面很弱,比如说:

  • 它的函数只能返回字串,无法返回数组
  • 它不支持面向对象,你无法实现一些优雅的设计模式
  • 它是解释型的,一边解释一边执行,连PHP那种预编译都不是,如果你的脚本包含错误(例如调用了不存在的函数),只要没执行到这一行,就不会报错

环境兼容性

如果你的脚本是提供给别的用户使用,使用sh或者bash,你的脚本将具有最好的环境兼容性,perl很早就是linux标配了,python这些年也成了一些linux发行版的标配,至于mac os,它默认安装了perl、python、ruby、php、java等主流编程语言。

第一个shell脚本

编写

打开文本编辑器,新建一个文件,扩展名为sh(sh代表shell),扩展名并不影响脚本执行,见名知意就好,如果你用php写shell 脚本,扩展名就用php好了。

输入一些代码,第一行一般是这样:

#!/bin/bash
#!/usr/bin/php

“#!”是一个约定的标记,它告诉系统这个脚本需要什么解释器来执行。

运行

运行Shell脚本有两种方法:

作为可执行程序

chmod +x test.sh
./test.sh

注意,一定要写成./test.sh,而不是test.sh,运行其它二进制的程序也一样,直接写test.sh,linux系统会去PATH里寻找有没有叫test.sh的,而只有/bin, /sbin, /usr/bin,/usr/sbin等在PATH里,你的当前目录通常不在PATH里,所以写成test.sh是会找不到命令的,要用./test.sh告诉系统说,就在当前目录找。

通过这种方式运行bash脚本,第一行一定要写对,好让系统查找到正确的解释器。

这里的"系统",其实就是shell这个应用程序(想象一下Windows Explorer),但我故意写成系统,是方便理解,既然这个系统就是指shell,那么一个使用/bin/sh作为解释器的脚本是不是可以省去第一行呢?是的。

作为解释器参数

这种运行方式是,直接运行解释器,其参数就是shell脚本的文件名,如:

/bin/sh test.sh
/bin/php test.php

这种方式运行的脚本,不需要在第一行指定解释器信息,写了也没用。

变量

定义变量

定义变量时,变量名不加美元符号($),如:

your_name="yangyh"

注意,变量名和等号之间不能有空格,这可能和你熟悉的所有编程语言都不一样。

除了显式地直接赋值,还可以用语句给变量赋值,如:

for file in `ls /etc`

使用变量

使用一个定义过的变量,只要在变量名前面加美元符号即可,如:

your_name="yangyh"
echo $your_name
echo ${your_name}

变量名外面的花括号是可选的,加不加都行,加花括号是为了帮助解释器识别变量的边界,比如下面这种情况:

for skill in Ada Coffe Action Java; do
echo "I am good at ${skill}Script"
done

如果不给skill变量加花括号,写成echo "I am good at $skillScript",解释器就会把$skillScript当成一个变量(其值为空),代码执行结果就不是我们期望的样子了。

推荐给所有变量加上花括号,这是个好的编程习惯。IntelliJ IDEA编写shell script时,IDE就会提示加花括号。

重定义变量

已定义的变量,可以被重新定义,如:

your_name="yangyh"
echo $your_name your_name="allen"
echo $your_name

这样写是合法的,但注意,第二次赋值的时候不能写$your_name="allen",使用变量的时候才加美元符。

注释

以“#”开头的行就是注释,会被解释器忽略。

多行注释

sh里没有多行注释,只能每一行加一个#号。就像这样:

#--------------------------------------------
# 这是一个自动打ipa的脚本,基于webfrogs的ipa-build书写:https://github.com/webfrogs/xcode_shell/blob/master/ipa-build # 功能:自动为etao ios app打包,产出物为14个渠道的ipa包
# 特色:全自动打包,不需要输入任何参数
#-------------------------------------------- ##### 用户配置区 开始 #####
#
#
# 项目根目录,推荐将此脚本放在项目的根目录,这里就不用改了
# 应用名,确保和Xcode里Product下的target_name.app名字一致
#
##### 用户配置区 结束 #####

如果在开发过程中,遇到大段的代码需要临时注释起来,过一会儿又取消注释,怎么办呢?每一行加个#符号太费力了,可以把这一段要注释的代码用一对花括号括起来,定义成一个函数,没有地方调用这个函数,这块代码就不会执行,达到了和注释一样的效果。

字符串

字符串是shell编程中最常用最有用的数据类型(除了数字和字符串,也没啥其它类型好用了,哈哈),字符串可以用单引号,也可以用双引号,也可以不用引号。单双引号的区别跟PHP类似。

单引号

str='this is a string'

单引号字符串的限制:

  • 单引号里的任何字符都会原样输出,单引号字符串中的变量是无效的
  • 单引号字串中不能出现单引号(对单引号使用转义符后也不行)

双引号

your_name='yangyh'
str="Hello, I know your are \"$your_name\"! \n"
  • 双引号里可以有变量
  • 双引号里可以出现转义字符

字符串操作

拼接字符串

your_name="yangyh"
greeting="hello, "$your_name" !"
greeting_1="hello, ${your_name} !" echo $greeting $greeting_1

获取字符串长度:

string="abcd"
echo ${#string} #输出:4

提取子字符串

string="chuchujie is a great company"
echo ${string:1:4} #输出:huch

查找子字符串

string="chuchujie is a great company"
echo `expr index "$string" is`#输出:8,这个语句的意思是:找出单词is在这名话中的位置

更多

参见本文档末尾的参考资料中Advanced Bash-Scripting Guid Chapter 10.1

数组

管道

条件判断

流程控制

和Java、PHP等语言不一样,sh的流程控制不可为空,如:

<?php
if (isset($_GET["q"])) {
search(q);
}
else {
//do nothing
}

在sh/bash里可不能这么写,如果else分支没有语句执行,就不要写这个else。

还要注意,sh里的if [ $foo -eq 0 ],这个方括号跟Java/PHP里if后面的圆括号大不相同,它是一个可执行程序(和cd, ls, grep一样),想不到吧?在CentOS上,它在/usr/bin目录下:

ll /usr/bin/[
-rwxr-xr-x. 1 root root 33408 6月 22 2012 /usr/bin/[

正因为方括号在这里是一个可执行程序,方括号后面必须加空格,不能写成if [$foo -eq 0]

if else

if

if condition
then
command1
command2
...
commandN
fi

写成一行(适用于终端命令提示符):

if `ps -ef | grep ssh`;  then echo hello; fi

末尾的fi就是if倒过来拼写,后面还会遇到类似的

if else

if condition
then
command1
command2
...
commandN
else
command
fi

if else-if else

if condition1
then
command1
elif condition2
command2
else
commandN
fi

for while

for

在开篇的示例里演示过了:

for var in item1 item2 ... itemN
do
command1
command2
...
commandN
done

写成一行:

for var in item1 item2 ... itemN; do command1; command2… done;

C风格的for

for (( EXP1; EXP2; EXP3 ))
do
command1
command2
command3
done

while

while condition
do
command
done

无限循环

while :
do
command
done

或者

while true
do
command
done

或者

for (( ; ; ))

until

until condition
do
command
done

case

case "${opt}" in
"Install-Puppet-Server" )
install_master $1
exit
;; "Install-Puppet-Client" )
install_client $1
exit
;; "Config-Puppet-Server" )
config_puppet_master
exit
;; "Config-Puppet-Client" )
config_puppet_client
exit
;; "Exit" )
exit
;; * ) echo "Bad option, please choose again"
esac

case的语法和C family语言差别很大,它需要一个esac(就是case反过来)作为结束标记,每个case分支用右圆括号,用两个分号表示break

函数

定义

调用

文件包含

可以使用source和.关键字,如:

source ./function.sh
. ./function.sh

在bash里,source和.是等效的,他们都是读入function.sh的内容并执行其内容(类似PHP里的include),为了更好的可移植性,推荐使用第二种写法。

包含一个文件和执行一个文件一样,也要写这个文件的路径,不能光写文件名,比如上述例子中:

. ./function.sh

不可以写作:

. function.sh

如果function.sh是用户传入的参数,如何获得它的绝对路径呢?方法是:

real_path=`readlink -f $1`#$1是用户输入的参数,如function.sh
. $real_path

用户输入

执行脚本时传入

脚本运行中输入

select菜单

stdin和stdout

常用的命令

sh脚本结合系统命令便有了强大的威力,在字符处理领域,有grep、awk、sed三剑客,grep负责找出特定的行,awk能将行拆分成多个字段,sed则可以实现更新插入删除等写操作。

ps

查看进程列表

grep

排除grep自身

查找与target相邻的结果

awk

sed

插入

替换

删除

xargs

curl

综合案例

参考资料

30分钟快速学习Shell脚本编程的更多相关文章

  1. 学习笔记之Linux Shell脚本教程:30分钟玩转Shell脚本编程

    Linux Shell脚本教程:30分钟玩转Shell脚本编程 http://c.biancheng.net/cpp/shell/

  2. Linux Shell脚本教程:30分钟玩转Shell脚本编程

    http://c.biancheng.net/cpp/shell/ Linux在线体验: http://compileonline.com/ Linux命令查询: http://man.linuxde ...

  3. 学习Shell脚本编程(第2期)_编写修改权限及执行Shell程序的步骤

    编写Shell程序 执行Shell程序 Shell程序有很多类似C语言和其他程序设计语言的特征,但是又没有程序语言那样复杂.Shell程序是指放在一个文件中的一系列Linux命令和实用程序.在执行的时 ...

  4. 学习Shell脚本编程(第5期)_表达式的比较

    字符串操作符 逻辑运算符 用test比较的运算符 数字比较符 文件操作符 在Shell程序中,通常使用表达式比较来完成逻辑任务.表达式所代表的操作符有字符操作符.数字操作符.逻辑操作符.以及文件操作符 ...

  5. 学习Shell脚本编程(第4期)_在Shell程序中的使用变量

    变量的赋值 变量的访问 变量的输入 4.1 变量的赋值     在Shell编程中,所有的变量名都由字符串组成,并且不需要对变量进行声明.要赋值给一个变量,其格式如下: 变量名=值  注意: 等号(= ...

  6. 学习Shell脚本编程(第3期)_在Shell程序中使用的参数

    位置参数 内部参数 如同ls命令可以接受目录等作为它的参数一样,在Shell编程时同样可以使用参数.Shell程序中的参数分为位置参数和内部参数等. 3.1 位置参数 由系统提供的参数称为位置参数.位 ...

  7. 学习Shell脚本编程(第1期)_Shell命令行书写规则

    Shell命令行的书写规则 对Shell命令行基本功能的理解有助于编写更好的Shell程序,在执行Shell命令时多个命令可以在一个命令行上运行,但此时要使用分号(:)分隔命令,例如: [root@l ...

  8. 学习Shell脚本编程(目录)

    所涉及的内容如下: Shell命令行的运行 编写.修改权限和执行Shell程序的步骤 在Shell程序中使用参数和变量 表达式比较.循环结构语句和条件结构语句 在Shell程序中使用函数和调用其他Sh ...

  9. 关于shell脚本编程的10个最佳实践

    每一个在UNIX/Linux上工作的程序员可能都擅长shell脚本编程.但大家解决问题的方式却不尽相同,这要取决于对专业知识的掌握程度.使 用命令的种类.看待问题的方式等等.对于那些处在shell脚本 ...

随机推荐

  1. 零基础如何一步一步开始搭建高性能直播平台?现以GitChat·架构来进行说明

    前言 现在直播已经成为移动互联网时代一个新的重要流量入口,从YY.斗鱼到花椒直播,直播已经成为人们分享交流的新方式,应用场景众多,主要分为: 金融类直播:金融直播可应用于实时解盘,在线专家讲座,专家在 ...

  2. RMI和socket详解

    详见: http://blog.yemou.net/article/query/info/tytfjhfascvhzxcytp58   一般来说,基于CS(client-server)软件架构的开发技 ...

  3. CCIE-MPLS VPN-实验手册(下卷)

    10:跨域的MPLS VPN (Option A) 10.1 实验拓扑 10.1 实验需求 a.       R1 R2 R3 组成P-NETWORK R1 R2 R3 位于AS 1,底层协议采用EI ...

  4. 转: 【Java并发编程】之十三:生产者—消费者模型(含代码)

    转载请注明出处:http://blog.csdn.net/ns_code/article/details/17249321 生产者消费者问题是线程模型中的经典问题:生产者和消费者在同一时间段内共用同一 ...

  5. 如何在C++中产生随机数

    C++中没有自带的random函数,要实现随机数的生成就需要使用rand()和srand().不过,由于rand()的内部实现是用线性同余法做的,所以生成的并不是真正的随机数,而是在一定范围内可看为随 ...

  6. Java-反射机制学习

    反射机制是Java的一个重要性,它使得Java语言具有了动态特性.比如说,可以在代码中动态地获取某个类的信息,生成它的实例.获取其成员变量.调用它的方法.下面通过几个示例来演示反射机制的作用与用法. ...

  7. 201521123096《Java程序设计》第八周学习总结

    1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结集合与泛型相关内容. 2. 书面作业 本次作业题集集合 1.List中指定元素的删除(题目4-1) 1.1 实验总结 实验中使用了s ...

  8. 201521123122 《java程序设计》 第四周学习总结

    1. 本周学习总结 1.1 尝试使用思维导图总结有关继承的知识点. 1.2 使用常规方法总结其他上课内容. 这个思维导图比较简单,详细内容点击此处 2. 书面作业 注释的应用 使用类的注释与方法的注释 ...

  9. 201521123062 《Java程序设计》第14周学习总结

    1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结多数据库相关内容. 2. 书面作业 1. MySQL数据库基本操作 建立数据库,将自己的姓名.学号作为一条记录插入.(截图,需出现自 ...

  10. 201521123114《Java程序设计》第9周学习总结

    1. 本章学习总结 2. 书面作业 Q1. 常用异常 题目5-1 1.1 截图你的提交结果(出现学号) 1.2 自己以前编写的代码中经常出现什么异常.需要捕获吗(为什么)?应如何避免? 经常出现的异常 ...