系统性学习shell脚本(2)- 基本语法

132 阅读13分钟

又刷了一遍权游,太好看了!!! (图应该是詹姆单枪刺龙妈)

前言

本文将从helloworld案例开始,逐步介绍shell的语法,并且附上案例,也包括其他的一些知识点。分支循环以及函数部分将在后面篇章介绍。

一、从Helloworld开始

目的: 写一个shell脚本,用于输出Helloworld

步骤

0. 创建一个文件夹用于以后放置shell脚本

mkdir datas

1. 创建一个shell脚本文件

cd datas
touch helloworld.sh

2. 使用vim文本编辑器编辑 helloworld.sh

vim helloworld.sh 

3. 文本文件内写上内容

image.png

4.执行对应shell脚本

几种实现方式

4.1 sh/bash 文件名

sh helloworld.sh
或者
bash helloworld.sh

4.2 sh/bash 文件绝对路径

bash /home/datas/helloworld.sh
或者
sh /home/datas/helloworld.sh

4.3 直接调用对应shell脚本

./helloworld.sh
4.3.1 权限不够怎么办

image.png

原因:如果使用sh/bash,就是让编译器去帮我们执行,编译器是有这个权限的。 如果我们自己直接去调用可能会权限不足。

查看一下我们对这个文件的权限

ll helloworld.sh

image.png

文件信息前面表示

image.png

解决: 给予当前用户执行权限,详细可见下面知识点内容。

由于我们当前用户就是文件属主,所以直接给予属主权限即可

第一种方式:使用数字赋予权限
chmod 764 helloworld.sh
第二种方式:使用符号赋予权限
chmod u=rwx,g=rw,o=r helloworld.sh 

二、多命令的shell脚本

写了helloworld之后,多命令也非常容易理解

目标: 在/home/hjw/下创建一个文本,文本中写入一段话 fire and blood"(最近在刷龙之家族,权游系列太好看了!!/(ㄒoㄒ)/~~)

1. 直接新建shell脚本如下

image.png 然后执行即可得到对应结果

三、系统变量以及自定义变量

1. 常见系统变量

系统变量含义用途
$HOME家目录用于找到当前用户的家目录
$PWD目前所在位置用于找到当前位置
$SHELL默认解析器找到默认解析器
$USER当前用户一般用于输出当前用户是谁

2. 自定义变量

2.1 基本语法

  • 定义变量: 变量=赋值。(等号左右是没有空格的!!!)
  • 使用变量: $变量名 或${变量名}
  • 取消变量: unset 变量
  • 定义静态变量: readonly 变量=赋值。(静态变量不可修改,不可unset)
  • 将变量设置为全局有效: export 变量

unset 和 readonly 实现如下

image.png

使用变量时,加与不加大括号的区别

当我们需要边界时,比如需要进行字符串拼接时 do my $homeworkdo my ${home}work是不同的,前者认为homework是一个变量,后者认为home是一个变量。

2.2 变量名定义规则

变量名的命名须遵循如下规则:

  • 命名只能使用英文字母,数字和下划线,首个字符不能以数字开头。
  • 中间不能有空格,可以使用下划线 _。
  • 不能使用标点符号。
  • 不能使用bash里的关键字(可用help命令查看保留关键字)。
  • 环境变量名建议大写

有效的 Shell 变量名示例如下:

RUNOOB
LD_LIBRARY_PATH
_var
var2

无效的变量命名:

?var=123
user*name=runoob

3.变量数据类型

3.1 字符串

  1. bash中默认的数据类型是字符串,所以无法进行运算。
  2. 对于含有空格的字符串,需要使用单引号或者双引号括起来。
  3. 可以使用 单引号和双引号区别

单引号:无法使用变量只会原样输出,单引号必须成对出现,转义单引号也不行。

双引号:可以使用变量,可以使用转义字符。

image.png

3.2 数组

bash支持一维数组(不支持多维数组),并且没有限定数组的大小。

类似于 C 语言,数组元素的下标由 0 开始编号。获取数组中的元素要利用下标,下标可以是整数或算术表达式,其值应大于或等于 0。

3.2.1 定义数组

在 Shell 中,用括号来表示数组,数组元素用"空格"符号分割开。定义数组的一般形式为:

数组名=(值12 ... 值n)

例如:

array_name=(value0 value1 value2 value3)

或者

array_name=(
value0
value1
value2
value3
)

还可以单独定义数组的各个分量:

array_name[0]=value0
array_name[1]=value1
array_name[n]=valuen

可以不使用连续的下标,而且下标的范围没有限制。

3.2.2读取数组

读取数组元素值的一般格式是:

${数组名[下标]}

例如:

valuen=${array_name[n]}

使用 @ 符号可以获取数组中的所有元素,例如:

echo ${array_name[@]}
3.2.3获取数组的长度

获取数组长度的方法与获取字符串长度的方法相同,例如:

实例
取得数组元素的个数  
length=${#array_name[@]}  
 或者  
length=${#array_name[*]}  
 取得数组单个元素的长度  
lengthn=${#array_name[n]}
3.2.4示例

image.png

4.特殊变量

4.1特殊变量 $n

$n含义:

  • n表示一个数字.
  • $0 表示当前文件名称
  • $1$9 表示脚本中第1到9 对应的参数
  • 十以上需要使用大括号如 ${10},也表示对应参数

参数一般用于输入到脚本中

案例: 输出输入的三个参数

  1. 编写脚本

image.png

  1. 执行脚本并且输入参数

image.png 结果如上,只会输出文件名 和 前三个参数,多的不会输出,少的也不会输出

4.2特殊变量 $#

$#含义: 用于获取参数个数,一般用在循环中

1.稍作修改shell脚本

image.png

2.执行脚本 image.png

4.3特殊变量 $* $@

$*:表示代表命令行中所有参数,并且当作一个整体看待。

$@:表示代表命令行中所有参数,但是区分看待。

直接输出两者没有任何差别,但是获取方式不同,会在分支循环中使用到

案例

image.png

image.png

4.4特殊变量 $?

$?:用于表明上一条命令是否正确执行

正确执行会是0

不正确会是1

image.png

四、运算符

前面说到,bash中默认的变量都是字符串,如果要进行运算怎么办? 以及一一些逻辑判断是咋写的?

1. 算数运算符

算数运算符用于数值计算,比如加减乘除取余等操作

1.1 语法
  • $((运算式)) 或者$[运算式] -expr +, -, \*, /, %, 加 减 乘 除 取余。
  • -注意 :乘法是 \*, 符号左右需要有空格
1.2 示例

复杂语句怎么办? 比如 (2+3)*4

  1. 直接expr ``expr 2 + 3 ``\* 4

image.png

  1. 使用括号方式

image.png 注意:这里的是直接*,而不是 \*

2. 关系运算符

关系运算符只支持数字,不支持字符串,除非字符串的值是数字

2.1 语法
运算符说明
-eq判断两个数是否相等,相等返回true
-ne判断两个数不相等,不相等返回true
-gt相当于大于号,如果左边大于右边返回true
-lt相当于小于号,右边大于左边返回true
-ge大于等于号,如果左边大于等于右边,返回true
-le小于等于号, 如果左边小于等于右边返回true
以上内容,如果不满足条件则为false
2.2 示例

这里写一个简单的示例,本来想用命令行来写。但是没找到合适的方法,于是就写一个shell脚本。 里面用到了后面的if判断语句,后面会介绍到。

image.png

3. 逻辑运算符

运算符说明
&&逻辑的与 AND
| |逻辑的或 OR
3.2示例

注意:当有两个表达式时,是两层中括号 image.png

4. 布尔运算符

运算符说明
非运算,相当于取反
-o或运算, 有一个表达式为true 则为true
-a与运算,表达式必须都为true,才为true

5. 字符串运算符

5.1 语法
运算符说明
=检测两个字符串是否相等,相等返回true
!=检测俩字符串是否不相等,不相等返回true
-z检测字符串长度是否为0,为0返回true
-n检测字符串长度是否为0, 不为0返回true
str检测字符串是否为空,不为空返回true
5.2 示例

image.png

6. 文件运算符

这里其实可以说是操作符

6.1 语法
操作符说明
-b file检测文件是否是块设备文件,如果是,返回true
-c file检测文件是否是字符设备文件,如果是,返回true
-d file检测文件是否是目录, 如果是,返回true
-f file检测文件是否是普通文件(不是目录也不是设备文件)如果是,返回true
-g file检测文件是否设置了 SGID位,如果是,则返回 true
-k file检测文件是否设置了粘着位(Sticky Bit),如果是,返回true
-p file检测文件是否是有名管道,如果是,则返回true
-u file检测文件是否设置了SUID位,如果是, 返回true
-r file检测文件是否可读,如果是,则返回true
-w file检测文件是否可写,如果是,则返回true
-x file检测文件是否可执行, 如果是,则返回true
-s file检测文件是否为空(文件发小是否大于零)不为空返回true
-e file检测文件(包括目录)是否存在,如果是,则返回true

使用起来比较简单

6.2示例

比较简单,将上面的例子改一下就好

image.png

遇到的问题

1. 在写布尔运算符示例时,总是报错"参数太多"

脚本如下 image.png 执行结果如下

image.png

原因: 当变量中有空格时,就会导致bash识别时造成误会。他会直接将对应变量值放在这个地方,因为 存在空格,导致变量中每一个单词都被认为是一个单独的参数。

解决一:去掉变量中的空格 image.png 亲测成功,但是会导致我们的代码不符合目的

解决二:将变量加上双引号 image.png 亲测成功。

知识点

以下内容用于复习Linux相关知识点,与shell无关

1. Linux的文件权限查看以及权限赋予

1.1 权限查看

在 Linux 中我们可以使用 ll 或者 ls –l 命令来显示一个文件的属性以及文件所属的用户和组,如:

[root@www /]# ls -l
total 64
dr-xr-xr-x   2 root root 4096 Dec 14  2012 bin
dr-xr-xr-x   4 root root 4096 Apr 19  2012 boot
……

实例中,bin 文件的第一个属性用 d 表示。d 在 Linux 中代表该文件是一个目录文件。

在 Linux 中第一个字符代表这个文件是目录、文件或链接文件等等。

  • 当为 d 则是目录
  • 当为 - 则是文件;
  • 若是 l 则表示为链接文档(link file);
  • 若是 b 则表示为装置文件里面的可供储存的接口设备(可随机存取装置);
  • 若是 c 则表示为装置文件里面的串行端口设备,例如键盘、鼠标(一次性读取装置)。

接下来的字符中,以三个为一组,且均为 rwx 的三个参数的组合。其中, r 代表可读(read)、 w 代表可写(write)、 x 代表可执行(execute)。 要注意的是,这三个权限的位置不会改变,如果没有权限,就会出现减号 - 而已。

每个文件的属性由左边第一部分的 10 个字符来确定(如下图)。

363003_1227493859FdXT

从左至右用 0-9 这些数字来表示。

第 0 位确定文件类型,第 1-3 位确定属主(该文件的所有者)拥有该文件的权限。

第4-6位确定属组(所有者的同组用户)拥有该文件的权限,第7-9位确定其他用户拥有该文件的权限。

其中,第 1、4、7 位表示读权限,如果用 r 字符表示,则有读权限,如果用 - 字符表示,则没有读权限;

第 2、5、8 位表示写权限,如果用 w 字符表示,则有写权限,如果用 - 字符表示没有写权限;第 3、6、9 位表示可执行权限,如果用 x 字符表示,则有执行权限,如果用 - 字符表示,则没有执行权限。

1.2 权限赋予

chmod:更改文件9个属性

Linux文件属性有两种设置方法,一种是数字,一种是符号。

Linux 文件的基本权限就有九个,分别是 owner/group/others(拥有者/组/其他)  三种身份各有自己的 read/write/execute 权限。

先复习一下刚刚上面提到的数据:文件的权限字符为: -rwxrwxrwx , 这九个权限是三个三个一组的!其中,我们可以使用数字来代表各个权限,各权限的分数对照表如下:

  • r:4
  • w:2
  • x:1

每种身份(owner/group/others)各自的三个权限(r/w/x)分数是需要累加的,例如当权限为: -rwxrwx--- 分数则是:

  • owner = rwx = 4+2+1 = 7
  • group = rwx = 4+2+1 = 7
  • others= --- = 0+0+0 = 0

所以等一下我们设定权限的变更时,该文件的权限数字就是 770。变更权限的指令 chmod 的语法是这样的:

 chmod [-R] xyz 文件或目录

选项与参数:

  • xyz : 就是刚刚提到的数字类型的权限属性,为 rwx 属性数值的相加。
  • -R : 进行递归(recursive)的持续变更,以及连同次目录下的所有文件都会变更

举例来说,如果要将  .bashrc 这个文件所有的权限都设定启用,那么命令如下:

[root@www ~]# ls -al .bashrc
-rw-r--r--  1 root root 395 Jul  4 11:45 .bashrc
[root@www ~]# chmod 777 .bashrc
[root@www ~]# ls -al .bashrc
-rwxrwxrwx  1 root root 395 Jul  4 11:45 .bashrc

那如果要将权限变成 -rwxr-xr-- 呢?那么权限的分数就成为 [4+2+1][4+0+1][4+0+0]=754。

符号类型改变文件权限

还有一个改变权限的方法,从之前的介绍中我们可以发现,基本上就九个权限分别是:

  • user:用户
  • group:组
  • others:其他

那么我们就可以使用 u, g, o 来代表三种身份的权限。

此外, a 则代表 all,即全部的身份。读写的权限可以写成 r, w, x,也就是可以使用下表的方式来看:

chmodu g o a+(加入) -(除去) =(设定)r w x文件或目录

如果我们需要将文件权限设置为 -rwxr-xr-- ,可以使用 chmod u=rwx,g=rx,o=r 文件名 来设定:

#  touch test1    // 创建 test1 文件
# ls -al test1    // 查看 test1 默认权限
-rw-r--r-- 1 root root 0 Nov 15 10:32 test1
# chmod u=rwx,g=rx,o=r  test1    // 修改 test1 权限
# ls -al test1
-rwxr-xr-- 1 root root 0 Nov 15 10:32 test1

而如果是要将权限去掉而不改变其他已存在的权限呢?例如要拿掉全部人的可执行权限,则:

#  chmod  a-x test1
# ls -al test1
-rw-r--r-- 1 root root 0 Nov 15 10:32 test1

1.3 chgrp:更改文件属组

语法:

chgrp [-R] 属组名 文件名

参数选项

  • -R:递归更改文件属组,就是在更改某个目录文件的属组时,如果加上-R的参数,那么该目录下的所有文件的属组都会更改。

1.4、chown:更改文件属主,也可以同时更改文件属组

语法:

chown [–R] 属主名 文件名
chown [-R] 属主名:属组名 文件名

进入 /root 目录(~)将install.log的拥有者改为bin这个账号:

[root@www ~] cd ~
[root@www ~]# chown bin install.log
[root@www ~]# ls -l
-rw-r--r--  1 bin  users 68495 Jun 25 08:53 install.log

将install.log的拥有者与群组改回为root:

[root@www ~]# chown root:root install.log
[root@www ~]# ls -l
-rw-r--r--  1 root root 68495 Jun 25 08:53 install.log

以上实践以及笔记内容皆为原创,知识点内容部分来自于网络,如有不足或者错误,请评论区或者私信指正。