当前位置: 首页 > news >正文

Linux下Shell脚本基础知识

主要参考视频:

这可能是B站讲的最好的Linux Shell脚本教程,3h打通Linux-shell全套教程,从入门到精通完整版_哔哩哔哩_bilibili

主要参考文档:

Shell 教程 | 菜鸟教程 (runoob.com)

Bash Shell教程 (yiibai.com)

先用视频入门,然后参考手册学习,之后实践总结。 

认识Shell和Bash

计算机里,我们基本都会使用操作系统,对于开发人员来说,我们肯定需要控制操作系统做一些事情,但是有个问题,那就是出于安全性考虑我们用户层没法直接访问操纵系统的内核,这种情况下,就有人提出解决方案,那就是在内核操作上套一层“壳”,也就是Shell,然后提供给用户一些指令,用户层再通过指令去完成要干的活,这样,就避免了直接操作内核,保证了内核的安全性以及系统的稳定性。

Shell命令就是由此而来,Shell命令按照一定格式组成的文本就是Shell脚本。

那Bash是怎么回事呢?

有Shell命令,那肯定要有命令的解释器,也就是将输入的命令转换成实际操作系统内核要干的活,其中,Bash就是Shell命令的解释器。

有了一种解释器,肯定就有它的优点和缺点,必然就会出现其他种类的解释器,各有各的特点,各有各的适用场景,于是,市场上就出现了多种Bash。

事实上,不同的Bash对应的Shell也有些不同。

Linux中默认使用Bash,我们操作Linux就需要学习各种Bash Shell。

Linux操作系统中有不同类型的Shell。其中一些如下:

  • Bourne Shell
  • C shell
  • Korn Shell
  • GNU Bourne Shell

要想知道操作系统支持哪种Shell类型,可在终端中输入以下命令:

cat /etc/shells

要想知道bash在操作系统中的位置,可键入以下命令,将获得一个特定的位置:

which bash

如下:

显示的都是绝对路径。 

Bash是Linux附带的标准Shell。它是当今最流行的开源Shell,并且具有在下一主题中阅读的各种生产功能。它也可用于Linux发行版,MacOS,Solaris 11和Windows 10。它通过许多改进为用户提供最佳体验。

Bash脚本

创建和执行

在计算机编程中,脚本是用于适当的运行时环境的一组命令,这些命令用于自动执行任务。

Bash Shell脚本是一个纯文本文件,其中包含一组通常在命令行中键入的各种命令。它用于在Linux文件系统上自动执行重复性任务。它可能包含一组命令或一个命令,或者可能包含命令式编程的标志,例如循环,函数,条件构造等。实际上,Bash脚本是用Bash编程语言编写的计算机程序。

如何创建和运行Bash脚本?

  1. 首先,使用cd命令进入保存脚本的目录;
  2. (如果已有脚本则跳过,否则)使用touch命令创建零字节大小的脚本,脚本以.sh作为扩展名(经验证,不以.sh结尾也可以,事实上,linux中没有扩展名的概念,之所以常常写扩展名,是为了方便人为查看);
    touch file_name
    
  3. 打开文本,并输入bash脚本的shell命令;
  4. 使用chmod赋予该脚本以可执行的权限(这一步别忘了,要不文件没有可执行权限);
  5. ./file_name来执行bash脚本(不在当前目录下就需要输入绝对路径或者相对路径);

比如,创建一个脚本,并输入如下内容:

#! /bin/bash
echo Hello World!

然后执行。

就会在终端打印出Hello World!

开头

每个基于Bash的Linux脚本都以以下行开头:

#!/bin/bash

这里#!称为shebang,该行的其余部分是解释器的路径,用于指定bash shell在操作系统中的位置。

shebang的格式很重要,格式不正确会导致命令工作不正常。因此,在创建脚本时,要始终记住SheBang格式的这两点:

  • 它应该始终在脚本的第一行。
  • #!和解释器的路径之间,#之前不应有任何空格。

经测试,貌似有空格也可以,不过,还是应当遵循标准规范。

注意,单独的#是注释。

注意,linux中路径是以撇/来分隔的,在windows下是以捺\作为分隔的。

echo

echo是bash中常用的linux命令

echo是Bash中的内置命令,用于通过传递参数来显示标准输出。它是用于将文本/字符串行打印到屏幕上的最广泛使用的命令。

echo命令 – 输出字符串或提取后的变量值 – Linux命令大全(手册) (linuxcool.com)

单独的echo可以打印出空行。

比如:

显示结果定向至某个文件,也就是将显示结果写到某个文件里,其中>是重定向符号。

echo "It is a test" > myfile

关于引号

关于有无引号?

如果字符串是个整体,其实可以不用引号,shell也能识别为一个字符串。

但是,当字符串中有空格时,就有问题了,这是因为Bash使用空格来确定单独的项目。 这种情况下,引号就可以用于处理带有空格字符的文本和文件名。

关于单引号和双引号?

使用简单的文本和字符串时,我们使用单引号或双引号都不会有任何区别

但是,当涉及到变量引用时,就有区别了。

shell变量扩展仅适用于双引号。如果在单引号中定义变量,则不会将其视为变量。

下面通过一个例子来理解这一点:

变量

变量部分内容可参考:

Shell 变量 | 菜鸟教程 (runoob.com)

之所以不能有空格,是因为bash是通过空格作为分隔的。

对于Bash,不必在变量声明时定义变量的数据类型。Bash变量是无类型的,只需通过分配其值来键入变量名称,它会自动判断数据类型。

如果将数字值分配给变量,它将自动转为整数工作,如果将字符值分配给该变量,则它将转为字符串类型。

year=2012  
comp_name=yiibai

使用echo命令,通过在名称之前加上美元($)号来读取它们,例如:

echo $year
echo $name

Shell或UNIX系统中都有两种类型的变量。

  • 系统定义的变量
  • 用户定义的变量

系统定义的变量

是由LINUX操作系统本身创建和维护的预定义变量。它们的标准约定是通常以大写字母进行定义。因此,每当看到以大写字母定义的变量时,很可能它们就是系统定义的变量。要了解系统中这些变量的列表,请在命令行终端上键入命令env或者printenv

用户定义的变量

这些变量由用户创建和维护。通常,这些类型的变量以小写形式定义。但是不强制的,也可以将变量名称写成大写。如下简单示例:

变量的操作

通常对Bash中的变量执行两个操作,如下所示:

  • 为变量设置值。
  • 读取变量的值。

设置变量值有几种方式,其中最常见的方法是直接设置值。

参考:Shell 变量 | 菜鸟教程 (runoob.com)

使用一个定义过的变量,只要在变量名前面加美元符号即可,如:

变量名外面的花括号是可选的,加不加都行,加花括号是为了帮助解释器识别变量的边界,比如下面这种情况:

如果不给skill变量加花括号,写成echo "I am good at $skillScript",解释器就会把$skillScript当成一个变量(其值为空),代码执行结果就不是我们期望的样子了。

推荐给所有变量加上花括号,这是个好的编程习惯。

Bash检查识别每个变量后,它将用分配的值替换每个变量名。它解释/运行每一行代码,并针对脚本的每个编码行继续执行此过程。

注:Bash中的两种变量都可以在终端以及Bash脚本上使用

只读变量

使用 readonly 命令可以将变量定义为只读变量,只读变量的值不能被改变。

下面的例子尝试更改只读变量,结果报错:

删除变量

使用 unset 命令可以删除变量。语法:

获取字符串长度

数组

参考:Shell 变量 | 菜鸟教程 (runoob.com) 

Shell 数组 | 菜鸟教程 (runoob.com)

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

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

定义数组

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

数组名=(值1 值2 ... 值n)

例如:

array_name=(value0 value1 value2 value3)

注意,数组不需要指定大小。

传递参数

参考:Shell 传递参数 | 菜鸟教程 (runoob.com)

我们可以在执行 Shell 脚本时,向脚本传递参数,脚本内获取参数的格式为 $n,n 代表一个数字,1 为执行脚本的第一个参数,2 为执行脚本的第二个参数。 

例如可以使用 $1、$2 等来引用传递给脚本的参数,其中 $1 表示第一个参数,$2 表示第二个参数,依此类推。

另外,还有几个特殊字符用来处理参数:

命令替换

其实就是获取命令执行的结果作为最终的内容。

替换的经典形式是使用反引号,如下所示:

也可以通过将命令放在圆括号(以美元符号($)开头)中来进行命令替换。如下:

示例:

读取用户输入

读取Bash用户输入,需要使用内置的Bash命令read。它用于从用户处获取输入并分配给变量。它仅从Bash shell中读取一行。以下是read命令的语法。

read <variable_name>

以下是从Bash脚本读取用户输入的示例:

运算符

Shell 和其他编程语言一样,支持多种运算符,包括:

  • 算数运算符
  • 关系运算符
  • 布尔运算符
  • 字符串运算符
  • 文件测试运算符

原生bash不支持简单的数学运算,但是可以通过其他命令来实现,例如 awk 和 expr,expr 最常用。

expr 是一款表达式计算工具,使用它能完成表达式的求值操作。

例如,两个数相加(注意使用的是反引号 ` 而不是单引号 '):

反引号,其实就是将expr命令执行的结果作为最终的结果值。

两点注意:

  • 表达式和运算符之间要有空格,例如 2+2 是不对的,必须写成 2 + 2,这与我们熟悉的大多数编程语言不一样。
  • 完整的表达式要被 ` ` 包含,注意这个字符不是常用的单引号,而是在 Esc 键下边的反引号。

更多直接参考

Shell 基本运算符 | 菜鸟教程 (runoob.com)

注意:注意:条件表达式要放在方括号之间,并且要有空格,例如: [$a==$b] 是错误的,必须写成 [ $a == $b ]。注意,这里面有四个空格,从方括号开始后面就有空格了。

比较新奇的一个字符串运算符

条件语句-if

注意:在 sh/bash 里,如果 else 分支没有语句执行,就不要写这个 else分支。

Shell 流程控制 | 菜鸟教程 (runoob.com)

以if开头,以fi结尾。

有if就要搭配then,最后分支的else可以不用。

条件放在方括号[……]里。我看两个参考文档里,有的说条件后要加分号,有的又没有加,比较混乱。参考下这篇:shell中的if语句 - 开始认识 - 博客园 (cnblogs.com)

再参考这篇:

Shell if else语句(详解版) (biancheng.net)

可知:

当then和if处于同一行时,分号;才是必须的。

双小括号 (( )) 是 Bash Shell 中专门用来进行整数运算的命令,它的效率很高,写法灵活,是企业运维中常用的运算命令。
注意:(( )) 只能进行整数运算,不能对小数(浮点数)或者字符串进行运算

选择语句-case

case ... esac 为多选择语句,与其他语言中的 switch ... case 语句类似,是一种多分支选择结构,每个 case 分支用右圆括号开始,用两个分号 ;; 表示 break,即执行结束,跳出整个 case ... esac 语句,esac(就是 case 反过来)作为结束标记。

可以用 case 语句匹配一个值与一个模式,如果匹配成功,执行相匹配的命令。

case ... esac 语法格式如下:

更多参考:

Shell 流程控制 | 菜鸟教程 (runoob.com)

循环语句

Shell 流程控制 | 菜鸟教程 (runoob.com)

for循环 while循环 until循环

for循环

shell三种 for循环方式_shell 循环100次-CSDN博客

参考:

Bash for循环 - Bash Shell教程 (yiibai.com)

注意,for-in中列表是以空格来分开每一项。

更多直接参考:

Shell 流程控制 | 菜鸟教程 (runoob.com)

函数

直接参考即可:

Bash函数 - Bash Shell教程 (yiibai.com)

文件包含

和其他语言一样,Shell 也可以包含外部脚本。这样可以很方便的封装一些公用的代码作为一个独立的文件。

Shell 文件包含的语法格式如下:

补充

当Shell脚本运行时,它会先查找系统环境变量ENV,该变量指定了环境文件(加载顺序通常是/etc/profile、~/.bash_profile、~/.bashrc、/etc/bashrc等),在加载了上述环境变量文件后,Shell就开始执行Shell脚本中的内容。

Shell脚本是从上至下、从左至右依次执行每一行的命令及语句的,即执行完了一个命令后再执行下一个,如果在Shell脚本中遇到子脚本(即脚本嵌套)时,就会先执行子脚本的内容,完成后再返回父脚本继续执行父脚本内后续的命令及语句。

通常情况下,在执行Shell脚本时,会向系统内核请求启动一个新的进程,以便在该进程中执行脚本的命令及子Shell脚本。

exit 命令退出shell,判断上一个命令是否执行成功,可用 echo $? ,为0,表示执行成功,不为0表示失败。失败时,可以使用exit命令退出整个shell脚本的进程。

Shell中函数不调用就不会被执行。 

shift,输入参数左移命令

Linux shell脚本中shift的用法说明_shell shift-CSDN博客 

shift命令用于对参数的移动(左移),通常用于在不知道传入参数个数的情况下依次遍历每个参数然后进行相应处理(常见于Linux中各种程序的启动脚本) 。

Linux—shell中$(( ))、$( )、``与${ }的区别 - chengd - 博客园 (cnblogs.com) 

dirname,获取路径的命令

Linux dirname命令教程:如何从给定的路径或文件名中提取目录(附实例用法详解和注意事项)-Linux入门自学网 (bashcommandnotfound.cn)

dirname命令是一个用于从给定的路径或文件名中提取目录部分的有用工具。

readlink命令

readlink命令 – 找出符号链接所指向的位置 – Linux命令大全(手册) (linuxcool.com)

readlink命令来自英文词组“read link”的拼写,中文译为“读取链接”,其功能是用于找出符号链接所指向的位置。

realpath,用于获取指定目录或文件的绝对路径。 

Linux 命令:realpath 命令-CSDN博客

trap命令用于指定在接收到信号后将要采取的动作,常见的用途是在脚本程序被中断时完成清理工作。

trap命令 – 指定采取的动作 – Linux命令大全(手册) (linuxcool.com)

相关文章:

  • 2024数据库期末综合解析(部分题)
  • Docker 安装 MySQL5.7 和 MySQL8
  • 10.Docker Compose容器编排
  • 数据结构01 栈及其相关问题讲解【C++实现】
  • 安全智能体的前沿技术研究与实践
  • 1527. 患某种疾病的患者
  • 如何根据CAP原理设计分布式系统
  • 集合面试题
  • 微信小程序地图
  • JavaFX BorderPane布局
  • 贪心算法学习五
  • Webrtc支持FFMPEG硬解码之解码实现(三)
  • 实战项目: 负载均衡
  • PostgreSQL如何使修改的参数生效
  • Java线程池的抛弃策略
  • 9月CHINA-PUB-OPENDAY技术沙龙——IPHONE
  • Effective Java 笔记(一)
  • ES6系统学习----从Apollo Client看解构赋值
  • Fastjson的基本使用方法大全
  • HomeBrew常规使用教程
  • Java-详解HashMap
  • JS数组方法汇总
  • Linux编程学习笔记 | Linux多线程学习[2] - 线程的同步
  • Nginx 通过 Lua + Redis 实现动态封禁 IP
  • oldjun 检测网站的经验
  • RxJS 实现摩斯密码(Morse) 【内附脑图】
  • TypeScript迭代器
  • 等保2.0 | 几维安全发布等保检测、等保加固专版 加速企业等保合规
  • 电商搜索引擎的架构设计和性能优化
  • 给Prometheus造假数据的方法
  • 计算机常识 - 收藏集 - 掘金
  • 聚簇索引和非聚簇索引
  • 前端
  • 使用putty远程连接linux
  • 小程序滚动组件,左边导航栏与右边内容联动效果实现
  • 一份游戏开发学习路线
  • 异常机制详解
  • 进程与线程(三)——进程/线程间通信
  • ​MySQL主从复制一致性检测
  • #include到底该写在哪
  • #stm32整理(一)flash读写
  • (2)nginx 安装、启停
  • (附源码)小程序儿童艺术培训机构教育管理小程序 毕业设计 201740
  • (简单有案例)前端实现主题切换、动态换肤的两种简单方式
  • (篇九)MySQL常用内置函数
  • (十七)devops持续集成开发——使用jenkins流水线pipeline方式发布一个微服务项目
  • (十一)JAVA springboot ssm b2b2c多用户商城系统源码:服务网关Zuul高级篇
  • (终章)[图像识别]13.OpenCV案例 自定义训练集分类器物体检测
  • (转)我也是一只IT小小鸟
  • ./configure,make,make install的作用
  • ./indexer: error while loading shared libraries: libmysqlclient.so.18: cannot open shared object fil
  • .Family_物联网
  • .Net FrameWork总结
  • .NET 中小心嵌套等待的 Task,它可能会耗尽你线程池的现有资源,出现类似死锁的情况
  • .net反混淆脱壳工具de4dot的使用