- 在学习下述内容之前,默认我们是已经了解Linunx系统和有关基础命令,才开始学习shell编程的。在我们开始编写自己的shell脚本之前,需要我们必须了解一些预备知识,此篇博客会介绍shell脚本的预备知识
使用多个命令
- 在此之前,我们输入Linux命令都是,输入一条命令,等待系统的输出,或进入此命令的内部环境,几乎没有同时操作多条命令的手段
- shell脚本的关键在于输入多个命令并处理每个命令的结果,甚至可以将一个命令的结果传给另一个命令。shell可以将多个命令串起来,一次执行完成。如果两个命令需要一起运行,可以将它们放在同一行中,彼此用分号(;) 隔开。如下:
date;who
- 如果你也完成了上述命令,那么你已经编写好了一个脚本。上述的简单脚本只用到了两个bash shell命令,
date
命令先运行,显示当前日期和时间,后面紧跟着who
命令的输出,显示当前是那个用户登录在系统上。使用这种办法就能将任意的多个命令串联在一起,只要不超过最大命令行字符数255就行 - 上述的分号分隔技术对于一些简单脚本尚可,但它也是有很大的缺陷:需要在每次运行之前,在命令行中输入整个命令,可以将这些命令组合成一个简单的文本文件,这样就不需要每次在命令行手动输入了。在需要运行这些命令时,只用运行这个文本文件即可
创建shell脚本文件
- 在创建shell脚本文件时,必须在文件的第一行指定要使用的shell,格式如下:
#!/bin/bash
- 在大部分的shell脚本中,
#(井号)
用作注释行,shell并不会处理shell脚本中的注释行。但是,shell脚本的第一行是个例外,#后面的!
会指定使用哪个shell来运行脚本,你可以使用bash shell,也可以指定使用另一类型的shell来运行你的脚本 - 在指定了shell之后,就可以在文件的每一行中输入命令,也可以使用
#
来添加注释。如下:
#!/bin/bash
# 输出当前时间及登陆系统的用户
date
who
- 上述就是当前脚本的所有内容了,可以根据需要将两个命令放在同一行上,但在shell脚本中你可以将命令书写在每一个独立的行中,shell会根据命令在文件中出现的顺序进行处理
- 还有,在当前脚本的第二行也是以
#
开头来添加注释,shell不会解释以#
开头的行(除了以#!
开头的第一行),此注释的作用仅仅只是说明当前脚本是要做什么,当很久之后我们回头看此脚本,可以通过此注释来了解脚本的作用 - 将此脚本保存在名为test1的文件中,然后运行脚本:
- 结果是
未找到当前test1的命令
,开头不顺利呀!! - 我们现在要解决的问题就是让bash shell能找到你的脚本文件,前置知识:shell会通过
PATH
环境变量来查找命令,快速查看PATH环境变量,如下:
echo $PATH
- PATH环境变量为一组目录, shell只会在PATH变量中展示的目录来查找脚本,要让shell找到test1脚本,只需使用一下两种方法:
- 将shell脚本文件所处的目录添加到PATH环境变量中
- 在提示符中用绝对或相对文件路径来引用shell脚本文件
注意:
-
一些Linux发行版将
$HOME/bin
目录添加进了PATH环境变量。它在每个用户的HOME目录下提供了一个存放文件的地方,shell可以在那里查找要执行的命令 -
上述问题,不推荐使用使用第一种方法,因为我们不能保证每次运行shell脚本都是在此添加到PATH环境变量的目录,每次在一个新的目录中运行shell脚本,都将目录添加到PATH环境变量吗?这样做是不现实的
-
我们采用第二种方法,将脚本文件的确切位置告诉shell,为了引用当前目录下的文件,可以使用
.
操作符,如下:
./test1
- 裂开,还是没有运行成功,它告诉我们
我们当前用户没有文件的执行权限
,快速查看当前文件的权限就能找到此问题的所在:
ll test1
- 在创建test1文件时,
umask
的值决定了新文件的默认权限设置,在我们当前使用的Ubuntu 20.04
中umask值被默认设置成了022,所以系统创建的文件是对于属主来说是没有执行权限的,使用chmod
命令赋予文件属主执行权限:
chmod u+x test1
./test1
- 如上显示,此脚本已经执行成功了!!!
显示消息
- 大多数shell命令都是换产生自己的输出,这些输出会显示在脚本所运行的控制台上,很多时候,我们可能想要告诉用户当前脚本正在做什么,可以使用
echo
命令来实现这一点,如果在echo
命令后面加上一个字符串,该命令就会在当前控制台输出显示当前字符串
echo 输出当前时间及登陆系统的用户
-
默认情况,是不需要使用引号将要显示文本字符串标注出来,但是有时在字符串中出现引号就比较麻烦了
-
echo命令可用单引号或双引号来划定文本字符串。如果在字符串中用到了它们,你需要在文本中使用其中一种引号,而用另外一种来将字符串划定起来
-
可以将
echo
语句添加到shell脚本的任何位置以此来显示需要额外展示的信息
-
执行脚本如下输出:
-
如果你想要将当前文本信息与命令的输出显示在同一行中,需要如何做?可以使用
echo -n
来实现。如下:
-
如下输出:
-
echo命令是shell脚本和用户交互的重要工具,在很多地方我们都会使用到它,尤其是需要显示脚本变量的值的时候,下面会介绍!!
使用变量
运行shell脚本中的单个命令自然有用,但是有自身的限制。通常你会需要在shell命令使用其他数据来处理信息。可以通过变量来实现。变量允许你临时性地将信息存储在shell脚本中,以便和脚本中的其他命令一起使用
环境变量
- 环境变量,用来记录特定的系统信息。比如系统的名称、登录到系统上的用户名、用户的系统ID(也称为UID)、用户的默认主目录以及shell查找程序的搜索路径。可以用
set命令
来显示一份完整的当前环境变量列表
set
- 上图并未将所有的的环境变量展示出来!!
- 在脚本中,你可以在环境变量名称之前加上
美元符($)
来使用这些环境变量,如下展示:
#!/bin/bash
# 展示系统用户信息
echo 用户名:$USER
echo 用户ID:$UID
echo 用户家目录:$HOME
- 注意,echo命令中的环境变量会在脚本运行时替换成当前值。另外,在第一个字符串中可以将
$USER系统变量
放置到双引号中,而shell依然能够知道我们的意图。但采用这种方法也还有一个问题,如下:
echo 衬衫的价格是 $99
- 显然这并不是我们想要的。只要shell脚本在引号中出现
美元符
,它就会以为你在引用一个变量。在上述例子中,脚本会尝试显示变量$9(但并未定义),再显示数字9。要显示美元符,你必须在$前面放置一个反斜线\
,来进行转义
- 反斜线在shell脚本将美元符转义为实际的美元符,而不是变量
注意:
- 你可能还见过通过
${variable}
形式引用的变量。变量名两侧额外的花括号通常用来帮助识别美元符后的变量名
用户变量
-
除了环境变量,shell脚本还允许在脚本中定义和使用用户的变量。定义变量允许临时存储数据并在整个脚本中使用,从而使shell脚本看起来更像一个真正的计算机程序
-
用户变量可以是任何由字母、数字或下划线组成的文本字符串,长度不超过20个。用户变量区分大小写,所以变量Var1和变量var1是不同的
-
使用等号将值赋给用户变量。一定注意(重要的事要说三遍!!!):
- 在变量、等号和值之间不能出现空格
- 在变量、等号和值之间不能出现空格
- 在变量、等号和值之间不能出现空格
-
shell脚本会自动决定变量值的数据类型。在脚本的整个生命周期里,shell脚本中定义的变量会一直保持着它们的值,但在shell脚本结束时会被删除掉,与系统变量类似,用户变量可通过美元符引用,如下展示:
#!/bin/bash
name=wy
age=18
echo 我是$name 今年$age
name=fbl
echo 我是$name 今年也$age
命令替换
- shell脚本中最有用的特性之一就是可以从命令输出中提取信息,并将其赋给变量。把输出赋给变量之后,就可以随意在脚本中使用了。这个特性在处理脚本数据时尤为方便
- 有两种方法可以将命令输出赋给变量:
- 反引号字符(`)
- $()格式
- 要注意反引号字符,这可不是用于字符串的那个普通的单引号字符。由于在shell脚本之外很少用到,你可能甚至都不知道在键盘什么地方能找到这个字符。但你必须慢慢熟悉它,因为这是许多shell脚本中的重要组件。提示:在美式键盘上,它通常和波浪线(~)位于同一键位
- 命令替换允许你将shell命令的输出赋给变量,要么用一对反引号把整个命令行命令围起来,要么使用$()格式:
testing=`date`
testing=$(date)
- shell会运行命令替换符号中的命令,并将其输出赋给变量testing。注意,赋值等号和命令替换字符之间没有空格,如下展示:
#/bin/bash
testing01=`date`
testing02=$(date)
echo 以下是使用两种方法
echo 方法1 $testing01
echo 方法1 $testing02
- 再举一个例子:在脚本中通过命令替换获得当前日期并用它来生成唯一文件名
#!/bin/bash
# 将/usr/bin目录下的文件重定向到log文件中
today=$(date +%y%m%d)
ls /usr/bin -al > log.$today
- today变量是被赋予格式化后的date命令的输出。提取日期信息来生成日志文件名常用的一种技术。
+%y%m%d
格式告诉date命令将日期显示为两位数的年月日的组合
- 这个脚本将日期值赋给一个变量,之后再将其作为文件名的一部分。文件自身含有目录列表的重定向输出。运行该脚本之后,应该能在目录中看到一个新文件
- 目录中出现的日志文件采用$today变量的值作为文件名的一部分。日志文件的内容是
/usr/bin
目录内容的列表输出。如果脚本在明天运行,日志文件名会是log.230404,就这样为新的一天创建一个新文件
注意
- 命令替换会创建一个子shell来运行对应的命令。子shell(subshell)是由运行该脚本的shell所创建出来的一个独立的子shell(child shell)。正因如此,由该子shell所执行命令是无法使用脚本中所创建的变量的
- 在命令行提示符下使用路径
./
运行命令的话,也会创建出子shell;要是运行命令的时候不加入路径,就不会创建子shell。如果你使用的是内建的shell命令,并不会涉及子shell。在命令行提示符下运行脚本时一定要留心