Linux Shell Script 基础教程

在这里插入图片描述

本文翻译自LEARN UNIX,博主在原文的基础上添加了一些内容。如果没有Linux 机器,推荐使用该网站 https://www.tutorialspoint.com/execute_ksh_online.php 作为shell在线demo的环境。

1. Shell 是什么

Shell为您提供了与Unix系统的接口。它收集您的输入,并根据该输入执行程序。程序完成执行后,将显示该程序的输出。
Shell 是可以运行我们的命令,程序,shell 脚本的环境。Shell 有不同的类型,每种Shell都有它自己的命令和功能。

1.1 Shell 类型

Linux 系统中,主要有两种类型的Shell:

  • Bourne shell - 如果使用Bourne类型的shell,默认的提示符就是$,可以通过修改环境变量PS1来更改你的提示符。
  • C shell - C 类型的shell,默认的提示符是%

Bourne Shell 又有下面几种类型:

  • Bourne shell (sh)
  • Korn shell (ksh)
  • Bourne Again shell (bash)
  • POSIX shell (sh)

C 类型 的Shell包括以下两种:

  • C shell (csh)
  • TENEX/TOPS C shell (tcsh)

在大多数 Linux 版本中,Bourne shell 通常都安装为/bin/sh。由于这个原因,它是不同版本的Linux 的首选 Shell。在本文中,我们将介绍大多数基于Bourne Shell的Shell概念。

1.2 Shell 脚本

Shell脚本的基本概念是命令列表,按执行顺序列出命令。
Shell 脚本文件以 .sh 结尾,例如 test.sh 。Shell 脚本的内容以 #! 开头,告诉系统接下来的命令将会被 Bourne Shell 执行。我们来创建一个Shell脚本,并往脚本中添加一点注释,注释以 # 开头:

#!/bin/bash

# Author: xueqiang.chen
# Script follows here:
pwd
ls

保存命令并执行脚本,可以看到以下输出内容:

$ chmod +x test.sh

$ ./test.sh
/home/centos
go.sh  test.sh

Shell 脚本可以有很复杂的结构,毕竟,shell是一种真正的编程语言,它包括变量,控制结构等。无论脚本变得多么复杂,它仍然只是顺序执行的命令的列表。

2. Shell 变量

变量是我们为其分配值的字符串,变量的值包括数字,文本,文件名,设备,或是任意的数据类型。变量只是实际数据的一个指针,我们可以创建,赋值,删除变量。

2.1 变量类型

当Shell运行时,存在三种主要类型的变量:

  • 局部变量:局部变量是存在于Shell当前实例中的变量。它不适用于由 Shell 启动的程序。它们在命令提示符下设置。
  • 环境变量:环境变量可用于Shell的任何子进程。某些程序需要环境变量才能正常运行。通常,shell脚本仅定义其运行的程序所需的那些环境变量。
  • Shell变量:Shell变量是Shell设置的特殊变量,shell要求Shell变量才能正常运行。这些变量中的一些是环境变量,而另一些是局部变量。

2.2 变量名

变量名称只能包含字母(a到z或A到Z),数字(0到9)或下划线字符(_)。按照约定,Unix shell变量将以大写字母命名。
下面的例子是一些有效和无效的变量名:

#!/bin/bash

# valid variable names
_ALL
TOKEN_A
VAR_1
VAR_2

# invaild variable names
2_VAR
-VARIABLE
VAR1-VAR2
VAR_A!

在shell 中,!, *, - 是含有特殊意义的,因此不能在变量名中使用。

2.3 定义变量

变量的定义方式如下:

variable_name=variable_value

注意,= 号两边不能有空格。

#!/bin/bash
NAME="Zara Ali"

这个例子中定义了一个变量,并给这个变量赋值,这种变量的类型称为标量, 标量只一次只能有一个值。

2.4 使用变量

Shell 中通过 $ 符号获取变量的值。例如,下面的例子将获取变量NAME的值并将其打印在标准输出中:

#!/bin/bash

NAME="Zara Ali"
echo $NAME

2.5 只读变量

Shell 允许通过 readonly 命令将一个变量变成只读变量。当变量成为只读变量后,它的值就不能更改了。

#!/bin/bash

NAME="Zara Ali"
readonly NAME
NAME="Qadiri"

上面的脚本会发生错误。

/bin/sh: NAME: This variable is read only.

2.6 重置变量

重置或删除变量将指示Shell程序从其跟踪的变量列表中删除该变量。重置变量后,将无法访问该变量中的存储值。
以下是使用 unset 命令取消定义的变量的语法:

unset variable_name

上面的命令重置定义的变量的值,下面的这个例子简单说明这个命令是如何工作的:

#!/bin/bash

NAME="Zara Ali"
unset NAME
echo $NAME

上面的例子不会打印出任何内容,你可以使用 unset 命令来重置被标记为 readonly 的变量。

3. 特殊变量

在上一节中,我们了解了在变量名称中使用某些非字母数字字符时应注意的事项。这是因为这些字符用在特殊的Unix变量的名称中。这些变量保留用于特定功能。

下面的表格列出了可以在我们的脚本中使用的特殊变量:

变量 说明
$0 当前脚本的文件名
$n | 这个变量对应于调用脚本的参数。其中参数 n 是正整数,代表参数的位置。例如第一个参数就是 $1 , 第二个参数就是 $2, 以此类推。
$# 脚本参数的数量
$* 输出整个参数列表,将整个列表作为一个参数,且之间使用空格隔开。
$@ | 与$* 的作用是一致的,不同的是该变量输出时会将参数列表分成单独的参数。
**`$ 变量
-------- ------------------------------------------------------------
** 当前shell的进程号。对于Shell脚本,这是它们执行时的进程ID。
$! 最后一个后台命令的进程号。
以下脚本使用与命令行相关的各种特殊变量:
#!/bin/sh

echo "File Name: $0"
echo "First Parameter : $1"
echo "Second Parameter : $2"
echo "Quoted Values: $@"
echo "Quoted Values: $*"
echo "Total Number of Parameters : $#"

运行结果:

$./test.sh Zara Ali
File Name : ./test.sh
First Parameter : Zara
Second Parameter : Ali
Quoted Values: Zara Ali
Quoted Values: Zara Ali
Total Number of Parameters : 2

3.1 错误状态

$? 变量表示上一个命令的退出状态。
退出状态是每个命令完成后返回的数值。通常,如果大多数命令成功,则返回退出状态0;如果不成功,则返回1。
某些命令出于特殊原因会返回其他退出状态。例如,某些命令区分错误的种类,并将根据故障的特定类型返回各种退出值。

下面的这个例子返回的是成功命令的状态:

$./test.sh Zara Ali
File Name : ./test.sh
First Parameter : Zara
Second Parameter : Ali
Quoted Values: Zara Ali
Quoted Values: Zara Ali
Total Number of Parameters : 2

$echo $?
0
$

4. 数组

Shell变量足以容纳单个值。这些变量称为标量变量。

Shell支持另一种类型的变量,称为数组变量。它可以同时保存多个值。数组提供了一种对一组变量进行分组的方法。数组的命名参考变量的命名规则。

4.1 定义数组

数组变量和标量变量之间的差异可以解释如下。
假设您尝试将各个学生的姓名表示为一组变量。每个单独的变量都是标量变量,如下所示:

#!/bin/bash
NAME01="Zara"
NAME02="Qadir"
NAME03="Mahnaz"
NAME04="Ayan"
NAME05="Daisy"

我们可以使用单个数组来存储所有上述名称。以下是创建数组变量的最简单方法。

array_name[index]=value

这里 array_name 是数组的名字,index 是要设置的数组中的索引, value 就是你要为该元素设置的值。如下所示:

#!/bin/bash

NAME[0]="Zara"
NAME[1]="Qadir"
NAME[2]="Mahnaz"
NAME[3]="Ayan"
NAME[4]="Daisy"

如果使用的是 bash shell ,也可以通过以下这种方法进行数组初始化:

array_name=(value1 ... valuen)

4.2 使用数组

为变量赋值之后,访问变量可以使用以下方式:

${array_name[index]}

这里 array_name 是数组名, index 是要访问那个数组项的索引。具体的例子如下:

#!/bin/bash
NAME[0]="Zara"
NAME[1]="Qadir"
NAME[2]="Mahnaz"
NAME[3]="Ayan"
NAME[4]="Daisy"
echo "First Index: ${NAME[0]}"
echo "Second Index: ${NAME[1]}"

echo "First Method: ${NAME[*]}"
echo "Second Method: ${NAME[@]}"

运行结果:

$./test.sh
First Index: Zara
Second Index: Qadir
First Method: Zara Qadir Mahnaz Ayan Daisy
Second Method: Zara Qadir Mahnaz Ayan Daisy

上面的示例中通过 *@ 来获取整个数组的值。

${array_name[*]}
${array_name[@]}

5. 运算符

每个Shell都支持不同的运算符,这里我们主要讨论的是 bash shell 的运算符。

5.1 算术运算符

Bourne shell 最初没有任何执行简单算术运算的机制,它使用 awkexpr 来进行计算。如下所示:

#!/bin/sh

val=`expr 2 + 2`
echo "Total value : $val"

运行结果:

Total value : 4

上面的例子我们需要注意的有两点:

  • 表达式和运算符之间必须用空格隔开,例如 2+2 是错误的,应该写成 2 + 2
  • 整个表达式要用反引号 `` 来包起来。

假设变量 a 等于 10, 变量 b 等于 20, 我们来看一下 bash shell 支持的算术运算符是如何计算这两个值得:

运算符 示例
+ expr $a + $b = 30
- expr $a - $b = -10
* expr $a \* $b = 200
/ expr $b / $a = 2
% expr $b % $a = 0
= a = $b 将b的值赋给a
== [ $a == $b ] 将会返回 false
!= [ $a != $b ]将会返回 true

注意:[ $a == $b ] 不能写成 [$a==$b]

5.2 关系运算符

Bash 支持以下特定于数值的关系运算符。
假设变量a = 10,变量b = 20,

运算符 描述 例子
-eq 检查运算符两边的值是否相等,相等返回 true [ $a -eq $b ] is not true.
-ne 检查运算符两边的值是否相等,不相等返回 true [ $a -ne $b ] is true
-gt gtgreater than 的缩写,检查运算符左边的值是否大于右边,是的话返回 true [ $a -gt $b ] is not true
-lt ltless than 的缩写,检查运算符左边的值是否小于右边,是的话返回true [ $a -lt $b ] is true
-ge gegreater than or equal 的缩写,检查运算符左边的值是否大于或等于右边的值,是的话返回true [ $a -ge $b ] is not true
-le leless than or equal 的缩写,检查运算符左边的值是否小于或等于右边的值,是的话返回true [ $a -le $b ] is true

注意:所有条件表达式应放在方括号内并在其周围留有空格。

5.3 布尔运算符

Bash 支持以下布尔运算符。假设变量 a 的值是 10, 变量 b 的值是20:

运算符 描述 示例
逻辑否。这会将真实条件转换为错误条件,反之亦然。 [ ! false ] is true.
-o 逻辑或。如果运算符两边之一为真,则条件为真。 [ $a -lt 20 -o $b -gt 100 ] is true.
-a 逻辑与。如果运算符两边都是真的,则条件为真。 [ $a -lt 20 -a $b -gt 100 ] is false.

5.4 字符串运算符

Bash 运算符支持以下操作。
假设变量 a 的值为 "abc",变量b的值为 "efg":

运算符 描述 示例
= 检查运算符两边的值是否相等;如果是,则条件变为真。 [ $a = $b ] is not true.
!= 检查运算符两边的值是否相等;如果值不相等,则条件为真 [ $a != $b ] is true.
-z 检查给定的字符串操作数大小是否为零;如果长度为零,则返回true。 [ -z $a ] is not true.
-n 检查给定的字符串操作数大小是否为非零;如果长度非零,则返回true。 [ -n $a ] is not false.
str Checks if str is not the empty string; if it is empty, then it returns false. [ $a ] is not false.

5.5 文件测试运算符

我们有一些运算符可用于测试与文件相关的各种属性。
假设有个文件变量 file 的值为一个存在的名为 "test" 的文件,该文件的大小为100bytes,并且有读写和执行的权限。

运算符 描述 示例
-b file 检查文件是否是一个块文件,如果是就返回true。 [ -b $file ] 返回 false。
-c file 检测文件是否是字符设备文件,如果是,则返回 true。 [ -c $file ] 返回 false。
-d file 检测文件是否是目录,如果是,则返回 true。 [ -d $file ] 返回 false。
-f file 检测文件是否是普通文件(既不是目录,也不是设备文件),如果是,则返回 true。 [ -f $file ] 返回 true。
-g file 检测文件是否设置了 SGID 位,如果是,则返回 true。 [ -g $file ] 返回 false。
-k file 检测文件是否设置了粘着位(Sticky Bit),如果是,则返回 true。 [ -k $file ] 返回 false。
-p file 检测文件是否是有名管道,如果是,则返回 true。 [ -p $file ] 返回 false。
-t file 检查文件描述符是否打开并与终端关联;如果是,则条件变为真。 [ -t $file ] is false.
-u file 检测文件是否设置了 SUID 位,如果是,则返回 true。 [ -u $file ] 返回 false。
-r file 检测文件是否可读,如果是,则返回 true。 [ -r $file ] 返回 true。
-w file 检测文件是否可写,如果是,则返回 true。 [ -w $file ] 返回 true。
-x file 检测文件是否可执行,如果是,则返回 true。 [ -x $file ] 返回 true。
-s file 检测文件是否为空(文件大小是否大于0),不为空返回 true。 [ -s $file ] 返回 true。
-e file 检测文件(包括目录)是否存在,如果是,则返回 true。 [ -e $file ] 返回 true。

5.6 其他shell的运算符

6. 条件表达式

在编写shell脚本时,可能需要从给定的两个路径中采用一个路径。因此,您需要使用条件语句,这些条件语句允许您的程序做出正确的决定并执行正确的操作。

7. 循环

循环是功能强大的编程工具,使您能够重复执行一组命令。在本章中,我们将研究以下可供Shell程序员使用的循环类型-

  • while 循环
  • for 循环
  • until 循环
  • select 循环

您将根据情况使用不同的循环。

7.1 嵌套循环

所有循环都支持嵌套概念,这意味着您可以将一个循环放入另一个类似的或不同的循环中。根据您的要求,此嵌套最多可以无限次。

这是嵌套while循环的示例。其他循环可以根据编程要求以类似的方式嵌套-

可以将while循环用作另一个while循环主体的一部分。
语法:

while command1 ; # this is loop1, the outer loop
do
   Statement(s) to be executed if command1 is true

   while command2 ; # this is loop2, the inner loop
   do
      Statement(s) to be executed if command2 is true
   done

   Statement(s) to be executed if command1 is true
done

示例:

#!/bin/sh

a=0
while [ "$a" -lt 10 ]    # this is loop1
do
   b="$a"
   while [ "$b" -ge 0 ]  # this is loop2
   do
      echo -n "$b "
      b=`expr $b - 1`
   done
   echo
   a=`expr $a + 1`
done

运行结果:

0
1 0
2 1 0
3 2 1 0
4 3 2 1 0
5 4 3 2 1 0
6 5 4 3 2 1 0
7 6 5 4 3 2 1 0
8 7 6 5 4 3 2 1 0
9 8 7 6 5 4 3 2 1 0
posted @ 2020-09-04 18:07  chenxueqiang  阅读(677)  评论(0编辑  收藏  举报