文章目录
在 Bash shell 中,每一个变量的值都是字符串,无论你给变量赋值时有没有使用引号,值都会以字符串的形式存储。
当然,如果有必要,你也可以使用 Shell declare 关键字显式定义变量的类型,但在一般情况下没有这个需求,Shell 开发者在编写代码时自行注意值的类型即可。
Shell 支持以下三种定义变量的方式:
variable 是变量名,value 是赋给变量的值。如果 value 不包含任何空白符(例如空格、Tab 缩进等),那么可以不使用引号;如果 value 包含了空白符,那么就必须使用引号包围起来。使用单引号和使用双引号也是有区别的,稍后我们会详细说明。
注意,赋值号=的周围不能有空格,这可能和你熟悉的大部分编程语言都不一样。
Shell 变量的命名规范和大部分编程语言都一样:
使用一个定义过的变量,只要在变量名前面加美元符号\(即可,如:</p><p>变量名外面的花括号{ }是可选的,加不加都行,加花括号是为了帮助解释器识别变量的边界,比如下面这种情况:</p><p>如果不给 skill 变量加花括号,写成echo “I am good at \)skillScript”,解释器就会把 \(skillScript 当成一个变量(其值为空),代码执行结果就不是我们期望的样子了。</p><p>推荐给所有变量加上花括号{ },这是个良好的编程习惯。</p><p>已定义的变量,可以被重新赋值</p><p>第二次对变量赋值时不能在变量名前加<span>,只有在使用变量时才能加</span>。</p><p>前面我们还留下一个疑问,定义变量时,变量的值可以由单引号’ '包围,也可以由双引号" "包围,它们到底有什么区别呢?不妨以下面的代码为例来说明:</p><p>以单引号’ '包围变量的值时,单引号里面是什么就输出什么,即使内容中有变量和命令(命令需要反引起来)也会把它们原样输出。这种方式比较适合定义显示纯字符串的情况,即不希望解析变量、命令等的场景。</p><p>以双引号" "包围变量的值时,输出时会先解析里面的变量和命令,而不是把双引号中的变量名和命令原样输出。这种方式比较适合字符串中附带有变量和命令并且想将其解析后再输出的变量定义。</p><p>Shell 也支持将命令的执行结果赋值给变量,常见的有以下两种方式:</p><p>第一种方式把命令用反引号 ``(位于 Esc 键的下方)包围起来,反引号和单引号非常相似,容易产生混淆,所以不推荐使用这种方式;第二种方式把命令用包围起来,区分更加明显,所以推荐使用这种方式。</p><p>例如,我在 demo 目录中创建了一个名为 log.txt 的文本文件,用来记录我的日常工作。下面的代码中,使用 cat 命令将 log.txt 的内容读取出来,并赋值给一个变量,然后使用 echo 命令输出。</p><p>使用 readonly 命令可以将变量定义为只读变量,只读变量的值不能被改变。</p><p>使用 unset 命令可以删除变量。语法:</p><p>变量被删除后不能再次使用;unset 命令不能删除只读变量。</p><p>shell 有一些内置的变量,混个眼熟:</p><p>shell 中的变量还有一点特性,可以设置默认值,即为了防止一些意外导致变量成为空值而导致脚本崩溃。</p><p>(1)\){var-default}:如果变量var没有被声明,那么就使用默认值,否则就是用var初始化的值
(2)\({var:-default}:如果变量var没有被声明或者已经声明,但是赋值为空,那么就使用默认值,否则就使用var初始化的值</p><p>(另外的写法:与与上述的两个是一样的)</p><p>(1)\){var+alt_value}:如果变量var被声明了, 无论var的值为空或者var初始化为一个值,都使用alt_value, 如果没有声明就为空.
(2)\({var:+alt_value}:如果变量var被初始化为一个非空的值, 那么就使用alt_value, 如果没有被声明或者已声明但初始化为空值就为空</p><p>(1)\){var?err_msg}:如果var已经被声明,那么就使用设置的值,否则打印err_msg错误消息。
(2)\({parameter:?err_msg}:如果parameter已经被初始化为一个非空的值, 那么就使用设置的值, 否则打印err_msg错误消息。</p><p>要看吐了吧,一个变量就这么多东西?别急。还有最后一个尾巴。</p><p>前期要慢一点,后期要快一点。</p><p>打基本功的时候不要嫌烦。</p><p>Shell 变量的作用域可以分为三种:</p><p>Shell 也支持自定义函数,但是 Shell 函数和 C++、Java、C# 等其他编程语言函数的一个不同点就是:在 Shell 函数中定义的变量默认也是全局变量,它和在函数外部定义变量拥有一样的效果。要想变量的作用域仅限于函数内部,可以在定义时加上local命令,此时该变量就成了局部变量。</p><p>所谓全局变量,就是指变量在当前的整个 Shell 进程中都有效。每个 Shell 进程都有自己的作用域,彼此之间互不影响。在 Shell 中定义的变量,默认就是全局变量</p><p>需要强调的是,全局变量的作用范围是当前的 Shell 进程,而不是当前的 Shell 脚本文件,它们是不同的概念。打开一个 Shell 窗口就创建了一个 Shell 进程,打开多个 Shell 窗口就创建了多个 Shell 进程,每个 Shell 进程都是独立的,拥有不同的进程 ID。在一个 Shell 进程中可以使用 source 命令执行多个 Shell 脚本文件,此时全局变量在这些脚本文件中都有效。</p><p>全局变量只在当前 Shell 进程中有效,对其它 Shell 进程和子进程都无效。如果使用export命令将全局变量导出,那么它就在所有的子进程中也有效了,这称为“环境变量”。</p><p>环境变量被创建时所处的 Shell 进程称为父进程,如果在父进程中再创建一个新的进程来执行 Shell 命令,那么这个新的进程被称作 Shell 子进程。当 Shell 子进程产生时,它会继承父进程的环境变量为自己所用,所以说环境变量可从父进程传给子进程。不难理解,环境变量还可以传递给孙进程。</p><p>注意,两个没有父子关系的 Shell 进程是不能传递环境变量的,并且环境变量只能向下传递而不能向上传递,即“传子不传父”。</p><p>创建 Shell 子进程最简单的方式是运行 bash 命令,如图所示:</p><p>通过exit命令可以一层一层地退出 Shell。</p><p>通过 export 导出的环境变量只对当前 Shell 进程以及所有的子进程有效,如果最顶层的父进程被关闭了,那么环境变量也就随之消失了,其它的进程也就无法使用了,所以说环境变量也是临时的。</p><p>有读者可能会问,如果我想让一个变量在所有 Shell 进程中都有效,不管它们之间是否存在父子关系,该怎么办呢?</p><p>只有将变量写入 Shell 配置文件中才能达到这个目的!Shell 进程每次启动时都会执行配置文件中的代码做一些初始化工作,如果将变量放在配置文件中,那么每次启动进程都会定义这个变量。</p><p>上面提到了,这里就补一下吧。</p><p>前面两种方式其实都是一样的:都是在当前父进程下的子进程中执行,子进程完成后,子进程中的各项变量或操作将会结束而不会传回到父进程中。</p><p>而通过第三种方式执行(source test.sh)的话,在父进程中就起作用了:</p><p>这就是直接执行与用source命令执行的区别,前者只作用于子进程本身,后者则作用于整个父进程。</p><p>因此:如要想不注销系统,并让全局配置文件生效,则必须用source命令。</p><p>注意:在执行时要赋权限</p><p>补充知识点:</p><p>一个规范的Shell脚本在第一行会指出由哪个程序(解释器)来执行脚本中的内容,这一行内容在Linux bash的编程一般为:</p><p>或</p><p>sh为bash的软链接,大多数情况下,脚本的开头使用“#! /bin/bash”和“#! /bin/sh”是没有区别的。</p><p>先看一下整数运算,这个相对来说不用花很多的时间。</p><p>直接上图,不想说话、</p><p>这里面选一个自己喜欢的用就好了,我选 (())</p><p> 在 (( )) 中使用变量无需加上\)前缀,(( )) 会自动解析变量名,这使得代码更加简洁,也符合程序员的书写习惯。
关系运算符和逻辑运算符等其他的运算符也先看一下,后面分支循环的时候才开始演示。
关系运算符:
逻辑运算符:
字符串运算符:
文件测试运算符:
以 # 开头的行就是注释,会被解释器忽略。
多行注释还可以使用以下格式:
EOF 也可以使用其他符号:
上点轻松的,接下来是一道硬菜。
echo 是一个 Shell 内建命令,用来在终端输出字符串,并在最后默认加上换行符。请看下面的例子:
echo 命令输出结束后默认会换行,如果不希望换行,可以加上-n参数。
默认情况下,echo 不会解析以反斜杠开头的转义字符。比如, 表示换行,echo 默认会将它作为普通字符对待,我们可以添加-e参数来让 echo 命令解析转义字符。例如:
shell 转义字符表:
先看一下,纯纯的基操:
对于字符串提取:
从指定位置开始截取 这种方式需要两个参数:除了指定起始位置,还需要截取长度,才能最终确定要截取的字符串。
既然需要指定起始位置,那么就涉及到计数方向的问题,到底是从字符串左边开始计数,还是从字符串右边开始计数。答案是 Shell 同时支持两种计数方式。
1、从字符串左边开始计数 如果想从字符串的左边开始计数,那么截取字符串的具体格式如下:
其中,string 是要截取的字符串,start 是起始位置(从左边开始,从 0 开始计数),length 是要截取的长度(省略的话表示直到字符串的末尾)。
2、 从右边开始计数 如果想从字符串的右边开始计数,那么截取字符串的具体格式如下:
同第 1) 种格式相比,第 2) 种格式仅仅多了0-,这是固定的写法,专门用来表示从字符串右边开始计数。
这里需要强调两点:
从指定字符(子字符串)开始截取 这种截取方式无法指定字符串长度,只能从指定字符(子字符串)截取到字符串末尾。Shell 可以截取指定字符(子字符串)右边的所有字符,也可以截取左边的所有字符。
1、 使用 # 号截取右边字符 使用#号可以截取指定字符(或者子字符串)右边的所有字符,具体格式如下:
其中,string 表示要截取的字符,chars 是指定的字符(或者子字符串),*是通配符的一种,表示任意长度的字符串。*chars连起来使用的意思是:忽略左边的所有字符,直到遇见 chars(chars 不会被截取)。
如果不需要忽略 chars 左边的字符,那么也可以不写*,例如:
注意,以上写法遇到第一个匹配的字符(子字符串)就结束了。例如:
如果希望直到最后一个指定字符(子字符串)再匹配结束,那么可以使用,具体格式为:
2、 使用 % 截取左边字符 使用%号可以截取指定字符(或者子字符串)左边的所有字符,具体格式如下:
请注意的位置,因为要截取 chars 左边的字符,而忽略 chars 右边的字符,所以应该位于 chars 的右侧。其他方面 % 和 # 的用法相同,这里不再赘述。
最后,我们对以上 8 种格式做一个汇总,请看下表:
注意,以上所有操作皆不会对原字符串造成任何实质性影响。若要保留结果,请赋值给另一个变量。
对于字符串替换: 命令已经在上面了,我这里就直接放示例吧:
关于字符串比较:
关于字符串拼接:
在 Shell 中你不需要使用任何运算符,将两个字符串并排放在一起就能实现拼接,非常简单粗暴。请看下面的例子:
是不是觉得 shell 的字符串也就仅此而已?那就大错特错了。因为我还没上那些专业的工具呢。后头会上。
常用的测试命令有以下三中方式,其中使用最多的是第二种。
以上表达式需要注意,Expression 指需要测试的表达式, 且前后必须要有空格,否则视为语法错误。 [] 和 [[]] 有所不同, [] 是命令,[[]] 是 Linux 中的关键字。
在 Linux 中可以使用 echo $? 查看上一条命令是否执行成功,其中 0 表示成功, 1-255 表示失败,每一个数字表示不同的失败原因。
关于那些运算符都在前面已经贴出。
if 分支:
单分支结构:
双分支结构:
多分支结构:
分支嵌套我就不贴了哈,
也可以把 then 单独放一行,那就不需要分号:
for 循环
do 和 done 之间的命令称为循环体,执行次数和list列表中常数或字符串的个数相同。for循环,首先将in 后 list 列表的第一个常数或字符串赋值给循环变量,然后执行循环体,以此执行 list;最后执行do 命令后的命令序列。 shell 支持列表 for 循环使用略写的计数方式,1~5 的范围用 {1…5} 表示(大括号不能去掉,否则会当作一个字符串处理)。 shell 中还支持按规定的步数进行跳跃的方式实现列表 for 循环,例如计算1~100内所有的奇数之和。
字符串for循环和数字for循环没有本质区别,只是形式有细微差别而已。下面列出几种常见的用法。
显示参数列表的所有单词:
显示list中的所有单词:
传入参数列表:
路径查找for循环: 通过for循环可以查找指定目录下的文件列表,并且可以使用通配符:
通配符查找指定路径:
通配符查找指定路径下符合指定扩展名的文件路径:
类C风格的for循环,和C没有太大的区别,这里只进行示例,不再赘述。
或者
结果:
计算1~5中数字的平方+1
我直接上实例吧: 1.利用while循环计算1到100的和:
示例代码2:利用while循环计算1到100之间所有奇数之和
示例代码3:利用while循环计算1到100之间所有偶数之和
2.利用while循环打印*
示例代码:利用while循环打印一个5x5的
3.使用read结合while循环读取文本文件:
示例代码1:
示例2:按列读取文件内容
4.while循环中的死循环:
示例:利用死循环,让用户做选择,根据客户的选择打印相应结果
这里的 swich 有点超纲,不过不急,马上就来:
case in 的 pattern 部分支持简单的正则表达式,具体来说,可以使用以下几种格式:
最后一个分支*)并不是什么语法规定,它只是一个正则表达式,表示任意字符串,所以不管 expression 的值是什么,)总能匹配成功。
下面的例子演示了如何在 case in 中使用正则表达式:
函数嘛,天天打交道的,直接上例子:
1、在shell文件内部定义函数并引用:
程序名:https://cloud.tencent.com/developer/article/factorial.sh,用于求阶乘 10的阶乘是:
2.返回值
函数返回码是指函数最后一条命令的状态码,可以用于函数返回值 使用return命令手动指定返回值:
由于shell状态码最大是255,所以当返回值大于255时会出错。
3.函数输出
为了返回大于255的数、浮点数和字符串值,最好用函数输出到变量:
4.向函数传递参数(使用位置参数):
5.全局变量与局部变量
默认条件下,在函数和shell主体中建立的变量都是全局变量,可以相互引用,当shell主体部分与函数部分拥有名字相同的变量时,可能会相互影响,例如:
在这种情况下,在函数内部最好使用局部变量,消除影响。
6.向函数传递数组变量:
7.函数返回数组变量
[
这篇已经迁延日久了,我 go 语言都学好了,接下来得上手 ansible 了,咳,卷吧。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/189028.html