脚本编程快速入门
Q: 什么是Shell脚本?
A:
Shell脚本(英语:Shell script),又称Shell命令稿、程序化脚本,是一种电脑程序使用的文本文件,内容由一连串的shell命令组成,经由Unix Shell直译其内容后运作。被当成是一种脚本语言来设计,其运作方式与解释型语言相当,由Unix shell扮演命令行解释器的角色,在读取shell脚本之后,依序运行其中的shell命令,之后输出结果。利用shell脚本可以进行系统管理,文件操作等。
通俗点来说, 就是将你想要执行的一系列命令放到一个文件当中, 这样:
- 逻辑更清晰: 因为如果你在命令行下面敲着一系列的命令的话, 每打一个命令, 会有一些输出, 你可能打着打着都忘了之前打的什么命令了, 而在一个脚本文件中, 你就只用管逻辑就行了
- 方便复用, 下次要执行这组命令时, 直接调用这个脚本文件就好了
Shell脚本类似于Windows中的批处理指令, 但不同之处在于它的效率更高, 因为它使用的是Linux下的命令.
Q: shell与shell脚本的区别?
A:
Shell就是一个命令行解释器,它的作用就是遵循一定的语法将输入的命令加以解释并传给系统。它为用户提供了一个向Linux发送请求以便运行程序的接口系统级程序,用户可以用Shell来启动、挂起、停止甚至是编写一些程序。 Shell本身是一个用C语言编写的程序,它是用户使用Linux的桥梁。Shell既是一种命令语言,又是一种程序设计语言(就是你所说的shell脚本)。作为命令语言,它互动式地解释和执行用户输入的命令;作为程序设计语言,它定义了各种变量和参数,并提供了许多在高阶语言中才具有的控制结构,包括循环和分支。它虽然不是 Linux系统内核的一部分,但它调用了系统内核的大部分功能来执行程序、创建文档并以并行的方式协调各个程序的运行。
简单来说, shell是一个服务, 一个用户可以通过它来访问操作系统内核的服务.
shell脚本是一个为shell编写的脚本程序, 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_0…9.txt文件
- 第7行:循环体结束
从这个示例可以看出, shell支持类似C语言里面的数组操作, 我还了解到, shell甚至还支持数组等操作
变量
-
定义变量:
定义变量时, 变量名不加
$, 例如:name="wang"注意: 变量名和等号之间不能有空格
变量命名规则:
- 首个字符必须为字母(a-z,A-Z)。
- 中间不能有空格,可以使用下划线(_)。
- 不能使用标点符号。
- 不能使用bash里的关键字(可用help命令查看保留关键字)。
还可以使用语句给变量赋值, 如下所示:
for file in `ls /etc` -
使用变量:
如果想要使用一个定义过的变量, 只用在变量名前面加
$即可echo $name echo ${name}变量名外的花括号可加可不加, 加花括号是为了帮助解释器识别变量的边界, 推荐加上
for skill in Java Python Shell Perl Ada; do echo "I am good at ${skill}Scripts" done -
重定义变量:
已经定义过的变量, 它的值是可以重新定义的, 也就是说, 它是一个可变变量.
注意: 第二次赋值时, 变量前面不要加
$ -
只读变量
在变量前面加
readonly命令可以将变量定义为只读变量,只读变量的值不能被改变。 -
删除变量
使用
unset命令可以删除变量, 变量被删除后不能再次使用;unset 命令不能删除只读变量。 -
变量类型
运行shell时,会同时存在三种变量: 1) 局部变量 局部变量在脚本或命令中定义,仅在当前shell实例中有效,其他shell启动的程序不能访问局部变量。
2) 环境变量 所有的程序,包括shell启动的程序,都能访问环境变量,有些程序需要环境变量来保证其正常运行。必要的时候shell脚本也可以定义环境变量。
3) shell变量 shell变量是由shell程序设置的特殊变量。shell变量中有一部分是环境变量,有一部分是局部变量,这些变量保证了shell的正常运行。
-
特殊变量
变量 含义 $0当前脚本的文件名 $n传递给脚本或函数的参数。n 是一个数字,表示第几个参数。例如,第一个参数是 $1,第二个参数是$2, 注意, 超过10需要加上{}。$#传递给脚本或函数的参数个数。 $*传递给脚本或函数的所有参数。 $@传递给脚本或函数的所有参数。被双引号(“ “)包含时,与 $*稍有不同$?上个命令的退出状态,或函数的返回值。所谓退出状态,就是上一个命令执行后的返回结果。 ?当前Shell进程ID。对于 Shell 脚本,就是这些脚本所在的进程ID。 注意:
$*与$@的区别$*和$@都表示传递给函数或脚本的所有参数,不被双引号(“ “)包含时,都以"$1" "$2" … "$n"的形式输出所有参数。但是当它们被双引号(“ “)包含时,”
$*“ 会将所有的参数作为一个整体,以”$1 $2 … $n“的形式输出所有参数;”$@“ 会将各个参数分开,以"$1" "$2" … "$n"的形式输出所有参数。 -
命令替换
命令替换是指Shell可以先执行命令,将输出结果暂时保存,在适当的地方输出。
#!/bin/bash DATE=`date` echo "Date is $DATE" -
变量替换
变量替换可以根据变量的状态(是否为空、是否定义等)来改变它的值。
形式 说明 ${var}变量本来的值 ${var:-word}如果变量 var为空或已被删除(unset),那么返回 word,但不改变var的值。${var:=word}如果变量 var为空或已被删除(unset),那么返回 word,并将var的值设置为 word。${var:?message}如果变量 var为空或已被删除(unset),那么将消息 message 送到标准错误输出,可以用来检测变量var是否可以被正常赋值。若此替换出现在Shell脚本中,那么脚本将停止运行。${var:+word}如果变量 var被定义,那么返回 word,但不改变 var 的值。
注释
以#开头的就是注释, 会被解释器所忽略
sh里面没有多行注释, 只能每一行加一个#
Q: 如果遇到大段代码需要临时不使用, 需要注释起来, 待会又要使用怎么办呢?
A:
方法一: 可以将这段代码放在一个函数里面, 如果没有地方调用到这个函数, 那么这段代码就不会执行(解释性语言的特性) 方法二: 可以利用编辑器的快捷键, 批量注释, 解除注释
字符串
字符串是编程里面最常用而且最有用的数据类型, 字符串可以用单引号, 也可以用双引号, 也可以不用引号
Q: 单引号与双引号的区别?
A:
- 单引号里面的任何字符都会原样输出, 单引号中的变量是无效的, 单引号字符串中不能出现单引号, 即使转义后也不行
- 双引号里面可以有变量, 双引号里面可以出现转义字符
字符串操作
-
拼接字符串
yourName="XiaoMing" greeting="hello, "${yourName}" !" greeting_1="hello, ${yourName} !" echo ${greeting} ${greeting_1} -
获取字符串长度
str="abcd" echo ${#str} #输出: 4 -
提取字符串
str="I am learning shellScripts" echo ${str:2:5} #输出从下标2开始的5个字符: am le -
查找字符串
str="I am learning shellScripts" echo `expr index "$str" is`#找出str中第一次出现i或者s的位置expr更多用法STRING : REGEXP anchored pattern match of REGEXP in STRING match STRING REGEXP same as STRING : REGEXP substr STRING POS LENGTH #从STRING中POS位置开始截取LENGTH个字符。POS索引是从1开始的。 index STRING CHARS #在STRING中查找字符CHARS首次出现的位置,没有找到返回0 length STRING #字符串长度
数组
bash支持一维数组(不支持多维数组),并且没有限定数组的大小。类似与C语言,数组元素的下标由0开始编号。获取数组中的元素要利用下标,下标可以是整数或算术表达式,其值应大于或等于0。
在Shell中,用括号来表示数组,数组元素用空格符号分割开。定义数组的一般形式为:
array_name=(value1 value2 ... valuen)
还可以单独定义数组的各个分量:
array_name[0]=value0
array_name[1]=value1
array_name[2]=value2
可以不使用连续的下标,而且下标的范围没有限制。
读取数组
echo ${array_name[2]} #读取下标为2的元素
echo ${array_name[*]} #读取所有元素
echo ${array_name[@]} #读取所有元素
echo ${#array_name[*]} #获取数组长度
echo ${#array_name[@]} #获取数组长度
echo ${#array_name[1]} #获取数组中单个元素的长度
运算符
-
算数运算符
expr是一款表达式计算工具,使用它能完成表达式的求值操作。注意这里很坑的几点:
- 表达式和运算符之间要有空格,例如
2+2是不对的,必须写成2 + 2,这与我们熟悉的大多数编程语言不一样。- 乘号(*)前边必须加反斜杠()才能实现乘法运算
- 完整的表达式要被
包含,注意这个字符不是常用的单引号,在 Esc 键下边。
expr 1 + 1
expr 2 - 1
expr 3 \* 2
expr 4 / 2
a=1
b=2
val=`expr $a \* $b`
echo ${val}
-
关系运算符
关系运算符只支持数字,不支持字符串,除非字符串的值是数字。
运算符 说明 -eq 检测两个数是否相等,相等返回 true。同算数运算符 ==-ne 检测两个数是否相等,不相等返回 true -gt 检测左边的数是否大于右边的,如果是,则返回 true。 -lt 检测左边的数是否小于右边的,如果是,则返回 true。 -ge 检测左边的数是否大等于右边的,如果是,则返回 true。 -le 检测左边的数是否小于等于右边的,如果是,则返回 true。 -
布尔运算符
运算符 说明 ! 非运算,表达式为 true 则返回 false,否则返回 true。 -o 或运算(or),有一个表达式为 true 则返回 true。 -a 与运算(and),两个表达式都为 true 才返回 true。 -
字符串运算符
运算符 说明 = 检测两个字符串是否相等,相等返回 true。 != 检测两个字符串是否相等,不相等返回 true。 -z 检测字符串长度是否为0,为0返回 true。 -n 检测字符串长度是否为0,不为0返回 true。 str 检测字符串是否为空,不为空返回 true。
流程控制
-
流程控制不可为空, 如果
else分支没有语句执行, 就不要写这个else -
sh里面的方括号是一个可执行程序, 所以方括号后面必须加空格
if [ $foo -eq 0 ] -
if else
if condition then command1 command2 ... commandN elif condition2 commanda else commandb fi当然, 也可以写成一行
if `ps -ef | grep ssh`; then echo hello; fi末尾结束的
fi就是if倒过来 -
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" esaccase的语法和C family语言差别很大,它需要一个esac(就是case反过来)作为结束标记,每个case分支用右圆括号,用两个分号表示break
-
continue与break
与C语言的一样
文件包含
可以使用source和.关键字
source ./func.sh
. ./func.sh
作用: 读入func.sh的内容, 并执行里面的内容
函数
函数可以让我们将一个复杂功能划分成若干模块,让程序结构更加清晰,代码重复利用率更高。像其他编程语言一样,Shell 也支持函数。Shell 函数必须先定义后使用
-
函数定义
function function_name () { list of commands [ return value ] }function关键字是可选的#!/bin/bash hello(){ echo 'hello'; } hello #输出 hello-
调用函数只需要给出函数名即可, 不需要添加括号
-
对于函数的返回值: 可以显式的增加
return语句, 如果不加, 会将最后一条命令的运行结果作为返回值 -
注意:return只能用来返回整数值
-
如果一定要让函数返回字符串,那么可以先定义一个变量,用来接收函数的计算结果,脚本在需要的时候访问这个变量来获得函数返回值。
-
-
删除函数
删除函数也可以使用
unset命令,不过要加上.f选项unset .f function_name -
函数参数
调用函数时可以向其传递参数。在函数体内部,通过
$n的形式来获取参数的值#!/bin/bash function sum(){ case $# in 0) echo "no param";; 1) echo $1;; 2) echo `expr $1 + $2`;; 3) echo `expr $1 + $2 + $3`;; *) echo "$# params! It's too much!";; esac } sum 1 3 5 6运行结果
4 params! It's too much!注意,
$10不能获取第十个参数,获取第十个参数需要${10}。当n>=10时,需要使用${n}来获取参数。另外,还有几个特殊变量用来处理参数
特殊变量 说明 $# 传递给函数的参数个数 $* 显示所有传递给函数的参数 $@ 与$*相同,但是略有区别,请查看Shell特殊变量 $? 函数的返回值
交互式
-
打印输出
echo: 是Shell的一个内部指令,用于在屏幕上打印出指定的字符串。
printf:格式化输出语句。它的可移植性更好
printf的语法:
printf format-string [arguments...] #format-string 为格式控制字符串,arguments 为参数列表。功能和用法与c语言的 printf 命令类似。与C语言printf的不同之处
- printf 命令不用加括号
- format-string 可以没有引号,但最好加上,单引号双引号均可。
- 参数比格式控制符(%)多时,格式控制符可以重用,可以将所有参数都转换。
- arguments 使用空格分隔,不用逗号。
-
读取输入
read: 命令行从输入设备读入内容
#!/bin/bash echo "What is your name?" read NAME #输入 echo "Hello, $NAME"
常用的命令
1. 字符处理三剑客:
,
, 
grep负责找出特定的行, awk将行拆分成多个字段, sed则实现更新, 插入, 删除等操作.
2. ps
查看进程列表
3. xargs
xargs命令的作用是将参数列表转换成小块分段传递给其他命令,以避免参数列表过长的问题。
Q: xargs与管道运算符的区别?
A:
管道符
|是将前一个命令的标准输出作为后一个命令的标准输入耳使用;而xargs命令是将前一个命令的标准输出作为后一个命令的参数而使用
4. curl
用来请求 Web 服务器。它的名字就是客户端(client)的 URL 工具的意思。
参考书籍:
- Shell脚本学习教程
- Shell脚本编程30分钟入门