shell初识

什么是shell?

Shell 是一个用 C 语言编写的程序,它是用户使用 Linux 的桥梁。Shell 既是一种命令语言,又是一种程序设计语言。
Shell 是指一种应用程序,一种和内核沟通的外壳应用程序的统称。这个应用程序有时提供了一个界面,用户通过这个界面访问操作系统内核的服务。

shell脚本开发环境

Shell 编程跟 java、php、python编程一样,只要有一个能编写代码的文本编辑器和一个能解释执行的脚本解释器就可以了。
Linux 的 Shell 解释器种类众多,常见的有: Bourne Shell(/usr/bin/sh或/bin/sh)Bourne Again Shell(/bin/bash)C Shell(/usr/bin/csh)K Shell(/usr/bin/ksh)Shell for Root(/sbin/sh)等
在linux中可以通过cat /etc/shells查看linux系统支持的shell的类型

[root@iZ8vbhcpwdmnwpx91dy1h8Z ~]# cat /etc/shells
/bin/sh
/bin/bash
/usr/bin/sh
/usr/bin/bash

如图就是Centos 7所支持的shell类型
我们关注的是 Bash,也就是 Bourne Again Shell,由于易用和免费,Bash 在日常工作中被广泛使用。同时,Bash 也是大多数Linux 系统默认的 Shell。

第一个Shell脚本

#!/bin/bash/
echo"hello shell!"

#! 是一个约定的标记,它告诉系统这个脚本需要什么解释器来执行,即使用哪一种 Shell。通常,(#!)的名称,叫做”Shebang”或者”Sha-bang”。
echo 命令用于向窗口输出文本。
注意:

如果脚本未指定shebang ,脚本执行的时候,默认用当前shell去解释脚本,即SHELL
如果 shebang 指定了可执行的解释器,如/bin/bash /usr/bin/python ,脚本在执行时,文件名会作为参数传递给解释器
如果#!指定的解释程序没有可执行权限,则会报错"Permission denied”。
如果#!指定的解释程序不是一个可执行文件,那么指定的解释程序会被忽略,转而交给当前的SHELL去执行这个脚本。
如果#!指定的解释程序不存在,那么会报错No such file or directory"。
#!之后的解释程序,需要写其绝对路径(如:#!/bin/bash),它是不会自动到$PATH中寻找解释器的。如果你使用""bash test.sh"这样的命令来执行脚本,那么#!这一行将会被忽略掉,解释器当然是用命令行中显式指定的bash。

但是该怎么样执行shell脚本呢?直接./吗?

[root@iZ8vbhcpwdmnwpx91dy1h8Z ~]# ./hello.sh
-bash: ./hello.sh: Permission denied

可以看到报错,因为没有执行权限,所以加一个执行权限chomd +x hello.sh

root@iZ8vbhcpwdmnwpx91dy1h8Z ~]# chmod +x hello.sh
[root@iZ8vbhcpwdmnwpx91dy1h8Z ~]# ./hello.sh
hello shell

可以看到可以执行hello.sh 脚本了
注意:一定要写成 ./hello.sh,而不是 hello.sh,运行其它二进制的程序也一样,直接写 hello.sh,linux 系统会去 PATH 里寻找有没有叫 test.sh 的,而只有 /bin, /sbin, /usr/bin,/usr/sbin 等在 PATH 里,你的当前目录通常不在 PATH 里,所以写成 hello.sh 是会找不到命令的,要用 ./test.sh 告诉系统说,就在当前目录找。
除了./执行还可以/bin/bash或者/bin/sh执行

[root@iZ8vbhcpwdmnwpx91dy1h8Z ~]# /bin/sh hello.sh
hello shell

这种运行方式是,直接运行解释器,其参数就是 shell 脚本的文件名,这种方式运行的脚本,不需要在第一行指定解释器信息,写了也没用。
Shell脚本中用#表示注释,相当于C语言的//注释。
但如果#位于第一行开头,并且是则例外,它表示该脚本使用后面指定的解释器/bin/bash解释执行。

解释执行本质原理

shell执行本质就是程序替换,这在前边进程控制已经介绍过了,就不再介绍了,这里就简单说一说执行过程吧。

第一种执行方式: chmod +x first.sh Shell会fork一个子进程并调用exec执行./firsh.sh这个程序,exec系统调用应该把子进程的代码段替换成./first.sh程序的代码段,并从它的_start开始执行
然而first.sh是个文本文件,根本没有代码段和_start函数! 怎么办呢? 其实exec还有另外一种机制,如果要执行的是一个文本文件,并且第一行用Shebang指定了解释器,则用解释器程序的代码段替换当前进程,并且从解释器的 _start开始执行,而这个文本文件被当作命令行参数传给解释器。
交互Shell(bash)fork/exec一个子Shell(sh)用于执行脚本,父进程bash等待子进程sh终止
sh读取脚本中的cd …命令,调用相应的函数执行内建命令,改变当前工作目录为上一级目录
sh读取脚本中的ls命令,fork/exec这个程序,列出当前工作目录下的文件,sh等待ls终止
ls终止后,sh继续执行,读到脚本文件末尾,sh终止。
sh终止后,bash继续执行,打印提示符等待用户输入。

现象
先看一段脚本代码

#!/bin/bashpwd
cd ..
pwd

这段脚本代码时先显示现在所处的路径接着返回到路径的上一层接着又获取现在所处的路径

[root@iZ8vbhcpwdmnwpx91dy1h8Z ~]# ./hello.sh
hello shell!

结果显示返回了当前路径的上一层

[root@iZ8vbhcpwdmnwpx91dy1h8Z ~]# ./test.sh
/root
/
[root@iZ8vbhcpwdmnwpx91dy1h8Z ~]# pwd
/root

但是又一次执行pwd显示路径没有改变,为什么呢?这是因为shell脚本在执行时创建子进程,让子进程解释shell脚本但是看一下下边的情况

[root@iZ8vbhcpwdmnwpx91dy1h8Z ~]# pwd
/root
[root@iZ8vbhcpwdmnwpx91dy1h8Z ~]# cd ../
[root@iZ8vbhcpwdmnwpx91dy1h8Z /]# pwd
/

这里为什么路径改变了?因为在执行命令时不一定创建子进程,执行这些命令并不需要创建子进程,这些叫做shell的内置命令,由父bash亲自执行,理解上,将该类命令,理解成shell的内部函数即可。
在看一个现象

[root@iZ8vbhcpwdmnwpx91dy1h8Z ~]# source test.sh
/root
/
[root@iZ8vbhcpwdmnwpx91dy1h8Z /]# pwd
/

在./或者source执行脚本时影响到了父bash。这是因为source和./时shell脚本的内建命令,这种方式在执行时不会创建子进程,而是直接在 交互式shell下执行脚本中的命令
那为什么子进程解释脚本不会影响父bash?
在shell脚本中,在运行 命令时会复制当前的shell环境并新建一个子shell环境。子shell环境有自己独立的 工作目录(pwd),继承原先shell环境中的alias和function。所以在执行cd …只是改变的是子进程自己的工作目录,并不影响父bash的工作目录。

shell变量

shell是弱类型语言,原则上,不是特别强调shell变量,或者shell变量可以放很多常见内容,这点和传统的C/C++有很大不同。shell变量也不需要提前定义,或者不牵扯到定义一说,需要时直接使用即可。

赋值和命名规则

name=“sigui”

注意,变量名和等号之间不能有空格! 否则会被Shell解释成命令和命令行参数。 同时,变量名的命名须遵循如下规则:

首个字符必须为字母(a-z,A-Z)。
中间不能有空格,可以使用下划线(_)。
不能使用标点符号。
不能使用bash里的关键字(可用help命令查看保留关键字)。

另外,shell变量可以放入你想放入的很多内容

#!/bin/bash
name="sigui"
echo "sigui"
age="25"
echo $name
echo $age[root@iZ8vbhcpwdmnwpx91dy1h8Z /]# source test.sh
sigui
sigui
25

所有的变量不需要先定义后使用,而是直接使用。

使用变量

如上,使用一个赋值过的变量,只要在变量名前面加美元符号即可 如

echo $name

但是有时候,会有这样的问题

#!/bin/bashname="hello shell"echo "$nameNIHAO"
[root@iZ8vbhcpwdmnwpx91dy1h8Z ~]# ./test.sh

如果想输出一个hello shellNiHAO但是shell将name和后边的字符串解释为一个变量名,因为这个变量没有定义所以输出了空,但时还是想输出这是该怎么办?

#!/bin/bash
name="hello shell
echo "${name} NIHAO"[root@iZ8vbhcpwdmnwpx91dy1h8Z ~]# ./test.sh
hello shell NIHAO

只需将变量名加一个{}加花括号是为了帮助解释器识别变量的边界

已定义的变量,可以被重新定义,如:

your_hobby="code"
echo $your_hobby
your_hobby="sleep"
echo $your_hobby

这样写是合法的,但注意,第二次赋值的时候不能写$your_hobby="sleep" 当变量作为右值时,才需要带上$ 符号。

只读变量

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

#!/bin/bashreadonly name="hello shell"echo "${name} NIHAO"name="hello word!" #对定义的变量重新定义
[root@iZ8vbhcpwdmnwpx91dy1h8Z ~]# ./test.sh
hello shell NIHAO
./test.sh: line 7: name: readonly variable

可以看到name时只读的不能对其修改

删除变量

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

#!/bin/bashname="hello shell"echo "${name} NIHAO"
unset name
echo "${name} NIHAO"
[root@iZ8vbhcpwdmnwpx91dy1h8Z ~]# ./test.sh
hello shell NIHAONIHAO

结果显示本来输出两行hello shell NIHAO只输出了一行unset后边的name内容没有输出,被删除的变量,内容会被清空,一般也是不在被使用的变量需要unset

unset 命令不能删除只读变量

#!/bin/bashreadonly name="hello shell"echo "${name} NIHAO"
unset name
echo "${name} NIHAO"
[root@iZ8vbhcpwdmnwpx91dy1h8Z ~]# ./test.sh
hello shell NIHAO
./test.sh: line 6: unset: name: cannot unset: readonly variable
hello shell NIHAO

结果显示只读数据无法被删除

变量类型

变量分为本地变量、环境变量、shell变量
本地变量: 局部变量在脚本或命令中定义,仅在当前shell实例中有效,其他shell启动的程序不能访问局部变量。
环境变量: 所有的程序,包括shell启动的程序,都能访问环境变量,有些程序需要环境变量来保证其正常运行。必要的时候shell脚本也可以定义环境变量。
shell变量: shell变量是由shell程序设置的特殊变量。shell变量中有一部分是环境变量,有一部分是局部变量,这些变量保证shell的正常运行
先看一个例子:

#!/bin/bash
echo "${name}"
[root@iZ8vbhcpwdmnwpx91dy1h8Z ~]# ./test.sh[root@iZ8vbhcpwdmnwpx91dy1h8Z ~]# name="hello shell"
[root@iZ8vbhcpwdmnwpx91dy1h8Z ~]# ./test.sh[root@iZ8vbhcpwdmnwpx91dy1h8Z ~]# echo $name
hello shell

在父bash上定义一个变量name但是子进程没有获取变量但是父bash自己可以获取

[root@iZ8vbhcpwdmnwpx91dy1h8Z ~]# export name
[root@iZ8vbhcpwdmnwpx91dy1h8Z ~]# ./test.sh
hello shell

将name写进环境变量中子进程可以获取到

本地变量

只存在于当前Shell进程,用set命令可以显示当前Shell进程中定义的所有变量(包括本地变量和环境变量)和函数。上面的name在没有被export 的时候,就是父shell内的一个本地变量,环境变量是任何进程都有的概念,而本地变量是Shell特有的概念
##环境变量**
环境变量可以从父进程传给子进程,因此Shell进程的环境变量 可以从当前Shell进程传给fork出来的子进程。用printenv命令可以显示当前Shell进程的环境变量。
一个变量定义后仅存在于当前Shell进程,它是本地变量,用export命令可以把本地变量导出为环境变量,定义和导出环境变量通常可以一步完成:
直接导入到环境变量

export name="shell"

或者先定义变量,再导入到环境变量

name="shell"
esport name

用unset命令可以删除已定义的环境变量或本地变量.

unset name

拼接字符串

原则上,只要将信息写在一起,就完成了string的拼接,当然,有一些特殊的地方,我们一一来学习一下:

[root@iZ8vbhcpwdmnwpx91dy1h8Z ~]# cat test.sh
#!/bin/bashstr1="hello"
str2=" shell"[root@iZ8vbhcpwdmnwpx91dy1h8Z ~]# ./test.sh
hello shell NIHAO
echo $str1$str2" NIHAO"

获取字符串长度

#!/bin/bash
str1="hello"echo ${#str1}
[root@iZ8vbhcpwdmnwpx91dy1h8Z ~]# ./test.sh
5

提取子字符串

从字符串第2个字符开始截取4个字符:

#!/bin/bash
str1="abcd123456789"echo ${str1:1:4}#前闭后开(]
[root@iZ8vbhcpwdmnwpx91dy1h8Z ~]# ./test.sh
bcd1

查找子字符串

[root@iZ8vbhcpwdmnwpx91dy1h8Z ~]# cat test.sh
#!/bin/bash
string="bit is a great company"
echo `expr index "$string" is`
[root@iZ8vbhcpwdmnwpx91dy1h8Z ~]# ./test.sh
2

文件名代换 (Globbing )** :

一篇彻底搞懂-->shell脚本相关推荐

  1. linux脚本第一行是什么,完全搞懂shell脚本第一行:#!/bin/bash的含义

    转自:https://blog.csdn.net/Doris0214/article/details/91453113 shell脚本的第一行一般会写有以下字样: #!/bin/bash 或者 #!/ ...

  2. 这一篇彻底搞懂JS中的prototype、__proto__与constructor真的很好

    文章目录 1. 前言 2. _ _ proto _ _ 属性 3. prototype属性 4. constructor属性 5. 总结 提示:不要排斥,静下心来,认真读完,你就搞懂了!(可以先看一下 ...

  3. 一篇文章学懂Shell脚本

    Shell脚本,就是利用Shell的命令解释的功能,对一个纯文本的文件进行解析,然后执行这些功能,也可以说Shell脚本就是一系列命令的集合. Shell可以直接使用在win/Unix/Linux上面 ...

  4. 《JVM系列》深入浅出类加载机制中<init>和<Clinit>的区别【一篇即可搞懂初始化机制】

    文章目录 前言:init和Clinit怎么产生的? 1.init方法 1.1.init方法什么时候被调用?用来做什么? 1.2.那么实例变量赋值操作.非静态代码块.构造器这三者,哪一个会先执行呢? 1 ...

  5. 一篇文章学懂Shell脚本,最简明的教程在这里

    Shell脚本,就是利用Shell的命令解释的功能,对一个纯文本的文件进行解析,然后执行这些功能,也可以说Shell脚本就是一系列命令的集合. Shell可以直接使用在win/Unix/Linux上面 ...

  6. linux 脚本$字符,一文看懂shell脚本中$0 $1 $# $@ $* $? $$ 的各种符号意义

    概述 shell中有两类字符,一类是普通字符,在Shell中除了本身的字面意思外没有其他特殊意义,即普通纯文本:另一类即元字符,是Shell的保留字符,在Shell中有着特殊的含义. 今天主要介绍一下 ...

  7. 一文读懂 shell 脚本编程

    一.shell 脚本基础知识 shell脚本是一个文件,里面存放的是特定格式的指令,系统可以使用脚本解析器翻译或解析指令并执行(无需编译).shell脚本的本质是 shell命令的有序集合 shell ...

  8. webpack5和webpack4详解(一篇全搞懂,值得收藏)

    文章目录 本篇的目的 1.webpack基础 1.1.webpack开发环境搭建 1.2.webpack配置核心 1.3.开发及生产环境分离 1.4.处理静态资源 2.webpack热门插件 2.1. ...

  9. 【Kubernetes】两篇文章 搞懂 K8s 的 fannel 网络原理

    近期公司的flannel网络很不稳定,花时间研究了下并且保证云端自动部署的网络能够正常work. 1.网络拓扑 拓扑如下:(点开看大图)  容器网卡通过docker0桥接到flannel0网卡,而每个 ...

  10. Linux疑难杂症解决方案100篇(五)-SHELL脚本中case语句的多种使用场景

    目录 掌握case语句的基本语法结构 掌握函数的定义及调用 掌握常用的正则表达式元字符含义 一.case语句 case语句为多重匹配语句 如果匹配成功,执行相匹配的命令 1. 语法结构 说明:patt ...

最新文章

  1. 【机器学习入门】(5) 决策树算法实战:sklearn实现决策树,实例应用(沉船幸存者预测)附python完整代码及数据集
  2. java注释风格 与javadoc
  3. wxWidgets之wxGrid控件
  4. python中isalpha的用法_python函数--isalpha()方法
  5. Swift中文教程(二十) 扩展
  6. 怎样取消隐式推送_九推,怎样寻找捡漏的机会?
  7. 云创大数据荣获2018年度南京白下高新园区统计工作先进单位
  8. SpringMVC 异常记录
  9. 安装cuda10.1
  10. [Android 4.4.3] 泛泰A860 Omni4.4.3 20140610 RC2.0 三版通刷 by syhost
  11. KEIL5 各个版本编译器的下载
  12. Android 如何测试你的Base64是否正确展示
  13. 网络随堂笔记2(计算机网络下三层硬件及拓扑结构)
  14. 单片机 cror crol
  15. 内存数据库及技术选型
  16. DoublyLinkedList
  17. smartbi连接mysql数据库_Smartbi_V9配置MySQL8作为知识库
  18. Unity3d游戏引擎Windy系列教程:常见组件扫盲讲解引入unity所需的脚本语言基础
  19. hysVideoQC v0.0.2.002版本发布
  20. 将canvas转化为图片

热门文章

  1. PhpStudy BackDoor 2019漏洞
  2. 后门攻击阅读笔记,Input-aware dynamic backdoor attack
  3. python excel数据分析师职业技能_数据分析师=Excel+Python?其实不止!
  4. 2021-04-25 AndroidStudio拖动条_小白龙抄作业
  5. 钉钉回放被管理员禁止下载?别急我来帮你
  6. 简单线性回归截距假设检验_统计推断——假设检验——简单线性回归分析
  7. 利用计算机进行导弹轨道计算,超级计算机为何被称为大国重器?可模拟核试验或测算弹道导弹轨迹!...
  8. 珍惜生命之水,节约从我做起
  9. matlab wmaxlev函数,CT-PET小波图像融合在精确放射治疗应用研究
  10. 特效制作思路(技术层面)