Shell脚本快速上手
示例
让我们从一个 Hello World 示例开始。
#!/bin/bash
echo "hello world" //屏幕输出
第一行被称作 hashbang 或 shebang。 它告诉 Unix 这个脚本应该通过 /bin/bash shell 运行。第二行是 *echo* 语句,它将后面的单词打印到终端。
介绍
shell和shell脚本的概念
shell是指一种应用程序,这个应用程序提供了一个界面,用户通过这个界面访问操作系统内核的服务。Ken Thompson的sh是第一种Unix Shell,Windows Explorer是一个典型的图形界面Shell。
shell脚本(shell script),是一种为shell编写的脚本程序。业界所说的shell通常都是指shell脚本,但读者朋友要知道,shell和shell script是两个不同的概念。
环境
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环境。
第一个shell脚本
编写
打开文本编辑器,新建一个文件,扩展名为sh(sh代表shell),扩展名并不影响脚本执行,见名知意就好,如果你用php写shell 脚本,扩展名就用php好了。
输入一些代码,第一行一般是这样:
#!/bin/bash
“#!”是一个约定的标记,它告诉系统这个脚本需要什么解释器来执行。
运行
运行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
这种方式运行的脚本,不需要在第一行指定解释器信息,写了也没用。
深入了解shell
现在我们已经了解了如何编写一个基本的 Hello World 示例,接下来让我们看看您在编写 shell 脚本时经常使用的一些语言结构。
变量
为了处理数据,数据必须保存在计算机的内存中。内存被划分为小的单元,每个单元都有一个唯一的编号,称为内存地址,用于存储数据
程序员可以为这个称为变量的内存地址提供唯一的名称。命名为存储位置的变量区域,该区域可以取不同的值,但每次只能取一个值。
在 Linux Shell 脚本中,有两种类型的变量:
系统变量 —— 由 Linux 本身创建和维护。这种类型的变量用大写字母定义。
用户定义的变量 —— 由用户创建和维护。这种类型的变量用小写字母定义。
可以在脚本中使用系统变量来显示这些变量包含的任何信息。像一些重要的系统变量:
BASH — 保存 shell 名称
BASH_VERSION — 保存 shell 版本名称
HOME — 保存主目录路径
OSTYPE — 保存操作系统类型
USERNAME – 保存当前登录到计算机的用户名
注意 — 上面的一些系统变量在不同的环境中可能有不同的值.
用户自定义的变量与其他编程语言中的变量一样简单,但是变量可以存储任何类型的数据,如下面的示例所示:
定义变量的语法
#!/bin/bash
name=abc
要访问用户自定义的变量,请使用以下语法:
访问变量的语法
#!/bin/bash
name=abc
$name
屏幕输出:
打印变量的语法
echo $name
#输出
abc
在字符串中使用上面的变量:
#打印变量的语法
echo "My name is $name"#输出
My name is abc
引号
下面是 Shell 脚本中可用的三种引号。
双引号("): 除了 \ 和 $ 外,双引号内的任何内容都是字符串。看下面例子:
#定义字符串变量双引号的语法
str="Shell scripting article"
echo $str#输出
Shell scripting article#使用 \ 来转义字符
str="Shell scripting \"article\""
echo $str#输出
Shell scripting "article"#在字符串中使用变量
user="ABC"
str="Shell scripting \"article\" by $user"
echo $str#输出
Shell scripting "article" by ABC
单引号('):单引号内的任何内容都是字符串。 看下面例子:
#使用单引号定义字符串变量的语法
str='Shell scripting article'
echo $str#输出
Shell scripting article#尝试在单引号中使用 \ 转义字符
str='Shell scripting \"article\"'
echo $str#输出
Shell scripting \"article\"
左引号 (`): 左引号内的任何内容都将被视为可执行命令。 看下面例子:
#定义字符串变量的语法
str='Current date is `date`'
echo $str#输出
Current date is Wed Apr 4 10:57:12 +04 2018
条件 [if/else]
Shell 脚本使用相当标准的 if 语句的语法。条件语句使用 test 命令或命令执行。
if 语句最基本的形式是:
#简单的 if then 语句的语法
if [ 35 -gt 0 ]
then
echo "Greater"
fi
# 输出Greater
你注意到 fi 的拼写是 if 倒过来的吗?参见下面包含 else 语句的示例:
#简单的 if then 语句的语法
if [ 35 -gt 45 ]
then
echo "Greater"
else
echo "Lesser"
fi
#输出Lesser
使用 elif 命令添加 else-if 语句结构。
#简单 if then else-if 语句的语法
if [ 35 -gt 55 ]
then
echo "Greater"
elif [ 35 -gt 45 ]
then
echo "Greater"
else
echo "Lesser"
fi
#输出Lesser
条件语句在 Shell 脚本中有许多不同的使用方法。下表详细说明了如何添加一些重要的比较:
字符串比较
+------------------+-----------------------------------------------+
| 条件 | 描述 |
+------------------+-----------------------------------------------+
| Str1 = Str2 | 如果字符串相等,则为真 |
| Str1 != Str2 | 如果字符串不相等,则为真 |
| -n Str1 | 如果字符串不为空,则为真 |
| -z Str1 | 如果字符串为空,则为真 |
+------------------+-----------------------------------------------+
数值比较
+------------------+-----------------------------------------------+
| 条件 | 描述 |
+------------------+-----------------------------------------------+
| expr1 -eq expr2 | 如果表达式相等,则为真 |
| expr1 -ne expr2 | 如果表达式不相等,则为真 |
| expr1 -gt expr2 | 如果表达式1大于表达式2,则为真 |
| expr1 -ge expr2 | 如果表达式1大于等于表达式2,则为真 |
| expr1 -lt expr2 | 如果表达式1小于表达式2,则为真 |
| expr1 -le expr2 | 如果表达式1小于等于表达式2,则为真 |
| !expr1 | 否定表达式的结果 |
+------------------+-----------------------------------------------+
这就是 shell 脚本中条件的基本用法。
循环
几乎所有的语言都有循环的概念,如果我们想要重复一个任务十次,我们不想要输入代码十次,每次可能会有一点变化。
因此,我们在 shell 脚本中有 for 和 while 循环。这比其他语言的特性要少一些。
For 循环:
#简单的 for 循环语句
for i in 1 2 3 4 5
do
echo "Hello world $i"
done
#输出
Hello world 1
Hello world 2
Hello world 3
Hello world 4
Hello world 5
上面的 for 循环首先创建变量 i,并从 1 到 5 的数字列表中给它分配一个数字,shell 对 i 的每次分配执行 echo 语句,在每次 迭代 中,它将回显输出中所示的语句。这个过程将持续到最后一项。
While 循环
While 循环将一直执行,直到条件为真。看下面的例子:
#简单的 While 循环语句
i = 1
while [ $i -le 5 ]
do
echo "Hello world $i"
i=`expr $i + 1`
done
# 输出
Hello world 1
Hello world 2
Hello world 3
Hello world 4
Hello world 5
上面的脚本首先创建一个值为 1 的变量 i。然后循环迭代直到 i 的值小于等于 5。语句
i=expr $i + 1 负责 i 的增值。
如果这个语句被删除,上面所说的循环将是一个 无限循环。
函数
函数( Function)是一种程序或处理过程类型。它封装了一个任务(逻辑组合了多个指令去完成确定的工作)并形成一条指令(一个供调用的函数名)。许多程序( programming) 语言本身提供许多内建的 函数(functions)。若没有这些函数,程序中要需要许多指令语句去完成某项任务。例如,计算一个数的平方。
在 shell 脚本中,有两种方式定义函数。
在本脚本文件里定义函数并调用。
创建单独的脚本文件包含供其他脚本调用的函数定义。例如, library.sh 文件里包含所有用到的函数。
在脚本文件中定义和使用函数,见下面的示例:
#声明和定义简单函数的语法
print_date()
{
echo "Today is `date +"%A %d %B %Y (%r)"`"
return
}
#调用上面定义的函数
print_date
#程序输出
Today is Thursday 05 April 2018 (12:11:23 PM)
函数调用和传参
Shell函数的调用和执行命令是一样的,只需输入函数名即可调用函数,函数必须在调用之前定义。
函数的参数利用位置参数进行获取与使用:
- 调用函数时,使用位置参数的形式为函数传递参数
- 函数内的
$1
-${n}
、$*
和$@
表示其接收的参数 - 函数调用结束后位置参数
$1
-${n}
、$*
和$@
将被重置为调用函数之前的值 - 在主程序和函数中,
$0
始终代表脚本名
funWithParam(){
echo "第一个参数为 $1 !"
echo "第二个参数为 $2 !"
echo "第十个参数为 $10 !"
echo "第十个参数为 ${10} !"
echo "第十一个参数为 ${11} !"
echo "参数总数有 $# 个!"
echo "作为一个字符串输出所有参数 $* !"
}
funWithParam 1 2 3 4 5 6 7 8 9 34 73
文件包含
如果你想在其它脚本里调用这个函数,可以使用source和.关键字,如:
source ./function.sh #function.sh为包含该函数的文件名
. ./function.sh
在bash里,source和.是等效的,他们都是读入function.sh的内容并执行其内容(类似PHP里的include),为了更好的可移植性,推荐使用第二种写法。
包含一个文件和执行一个文件一样,也要写这个文件的路径,不能光写文件名,比如上述例子中:
. ./function.sh
不可以写作:
. function.sh