文章目录
Shell简介
Shell编程是一种使用Shell(命令行解释器)来编写脚本和程序的方法。Shell是用户与操作系统交互的接口,它允许用户通过命令行输入指令来执行程序、管理文件系统中的文件和目录等。Shell脚本是一种为Shell编写的脚本程序,用于自动化重复性任务、系统管理任务等。Shell 是一个用 C 语言编写的程序,它是用户使用 Linux 的桥梁。Shell 既是一种命令语言,又是一种程序设计语言。
Shell环境
Shell 编程跟 JavaScript、php 编程一样,只要有一个能编写代码的文本编辑器和一个能解释执行的脚本解释器就可以了。
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)
在一般情况下,人们并不区分 Bourne Shell 和 Bourne Again Shell,所以,像 #!/bin/sh,它同样也可以改为 #!/bin/bash。
#! 告诉系统其后路径所指定的程序即是解释此脚本文件的 Shell 程序
第一个Shell脚本
打开文本编辑器(可以使用 vi/vim 命令来创建文件),新建一个文件 test.sh,扩展名为 sh(sh代表shell),扩展名并不影响脚本执行,见名知意就好。 输入一些代码,第一行一般是这样:
代码解析
#! 是一个约定的标记,它告诉系统这个脚本需要什么解释器来执行,即使用哪 一种 Shell。
echo 命令用于向窗口输出文本。
Shell脚本的运行方法
作为可执行程序,将上面的代码保存为 test.sh,并 cd 到相应目录:
注意,一定要写成 ./test.sh,而不是 test.sh,运行其它二进制的程序也一样,直接写 test.sh,linux 系统会去 PATH 里寻找有没有叫 test.sh 的,而只有 /bin, /sbin,/usr/bin,/usr/sbin 等在 PATH 里,你的当前目录通常不在 PATH 里,所以写成 test.sh 是会找不到命令的,要用 ./test.sh 告诉系统说,就在当前目录找
作为解释器参数,这种运行方式是,直接运行解释器,其参数就是 shell 脚本的文件名,如:
基础语法
Shell变量
变量是任何一种编程语言都必不可少的组成部分,变量用来存放各种数据。脚本语言在定义变量时通常不需要指明类型,直接赋值就可以,Shell 变量也遵循这个规则。
在 Bash shell 中,每一个变量的值都是字符串,无论你给变量赋值时有没有使用引号,值都会以字符串的形式存储。
这意味着,Bash shell 在默认情况下不会区分变量类型,即使你将整数和小数赋值给变量,它们也会被视为字符串,这一点和大部分的编程语言不同。例如在C语言或者 C++ 中,变量分为整数、小数、字符串、布尔等多种类型。当然,如果有必要,你也可以使用 Shell declare 关键字显式定义变量的类型,但在一般情况下没有这个需求,Shell 开发者在编写代码时自行注意值的类型即可。
定义变量
Shell 支持以下三种定义变量的方式:
- variable=value
- variable=‘value’
- variable=“value”
注意,赋值号=的周围不能有空格,这可能和你熟悉的大部分编程语言都不一样。
Shell 变量的命名规范和大部分编程语言都一样:
- 变量名由数字、字母、下划线组成;
- 必须以字母或者下划线开头;
- 不能使用 Shell 里的关键字(通过 help 命令可以查看保留关键字)。
使用变量
使用一个定义过的变量,只要在变量名前面加美元符号$即可,如:
变量名外面的花括号{ }是可选的,加不加都行,加花括号是为了帮助解释器识别变量的边界,比如下面这种情况:
如果不给 skill 变量加花括号,写成echo “I am good at $Web”,解释器就会把 $skillWeb 当成一个变量(其值为空),代码执行结果就不是我们期望的样子了。
推荐给所有变量加上花括号{ },这是个良好的编程习惯。
修改变量的值
已定义的变量,可以被重新赋值,如:
第二次对变量赋值时不能在变量名加$ ,只有在使用变量时才能加$。
单引号和双引号的区别
单引号(') | 双引号(") | |
---|---|---|
引用类型 | 强引用 | 弱引用 |
变量替换 | 不替换 | 替换 |
命令替换 | 不执行 | 执行(但需注意反引号与$()的区别) |
特殊字符处理 | 字面化 | 部分特殊字符被解释和扩展 |
应用场景 | 适用于需要原样输出字符串的场景 | 适用于需要变量替换和简单命令执行的场景 |
在编写Shell脚本时,应根据具体需求选择使用单引号还是双引号来引用字符串。如果需要保持字符串的原始内容不变,包括其中的变量和命令,则应使用单引号。如果需要在字符串中嵌入变量或执行命令,并希望它们被替换或执行,则应使用双引号。
将命令的结果赋值给变量
Shell 也支持将命令的执行结果赋值给变量,常见的有以下两种方式:
- variable=
shell
- variable=$(Shell)
第一种方式把命令用反引号
(位于 Esc 键的下方)包围起来,反引号和单引号非常相似,容易产生混淆,所以不推荐使用这种方式;第二种方式把命令用$()包围起来,区分更加明显,所以推荐使用这种方式。
只读变量
在Shell中,只读变量(read-only variable)是一种一旦被赋值后,其值就不能被改变的变量。这在你想要保护某些关键配置或数据不被意外修改时特别有用。在Bash(最常见的Shell之一)中,你可以使用readonly命令或declare -r命令来声明一个只读变量。
删除变量
使用 unset 命令可以删除变量。语法:
变量被删除后不能再次使用;unset 命令不能删除只读变量。
Shell传递参数
我们可以在执行 Shell 脚本时,向脚本传递参数,脚本内获取参数的格式为:$n。n 代表一个数字,1 为执行脚本的第一个参数,2 为执行脚本的第二个参数,以此类推……
例子:以下实例我们向脚本传递三个参数,并分别输出,其中 $0 为执行的文件名(包含文件路径):
#!/bin/sh
#接收文件外部传参
#超过十的数字用括号括起来
echo "接收第一个参数$0"
echo "接收第二个参数$1"
echo "接收第三个参数$2"
echo "接收第四个参数$3"
echo "接收第五个参数$4"
echo "接收第六个参数$5"
特殊字符处理参数说明
举例:
#!/bin/sh
echo "接收第二个参数$1"
echo "$# 个输出"
echo "输入的参数为$*"
echo "输入的参数为$@"
执行脚本,输出结果如下所示:
$* 与 $@ 区别
∗和@ 都是 Shell 脚本中的特殊变量,用于表示传递给函数或脚本的所有参数。它们之间的区别主要体现在如何处理这些参数上,特别是在被双引号包围时。
未被双引号包围时:
- 在这种情况下,$* 和 $@ 几乎没有区别。它们都会将接收到的每个参数视为独立的,彼此之间以空格分隔。例如,如果传递给脚本的参数是 a b c,那么无论是 $* 还是 $@,在未被双引号包围时,它们都会被视为三个独立的参数。
被双引号包围时:
- 当 $* 被双引号包围时,它会将所有的参数视为一个整体,即一个由 IFS分隔的单一字符串。这意味着,如果参数之间有空格,这些空格也会被包含在内,并且整个字符串被视为一个整体。
- 而 $@ 即使被双引号包围,也会保持每个参数的独立性。每个参数都会被视为一个独立的字符串,彼此之间以空格分隔。
Shell字符串
字符串(String)就是一系列字符的组合。字符串是 Shell 编程中最常用的数据类型之一(除了数字和字符串,也没有其他类型了)
字符串可以由单引号’ '包围,也可以由双引号" "包围,也可以不用引号。它们之间是有区别的。
三种形式的区别
-
由单引号’ '包围的字符串:
- 任何字符都会原样输出,在其中使用变量是无效的。
- 字符串中不能出现单引号,即使对单引号进行转义也不行。
-
由双引号" "包围的字符串:
- 如果其中包含了某个变量,那么该变量会被解析(得到该变量的值),而不是原样输出。
- 字符串中可以出现双引号,只要它被转义了就行。
-
不被引号包围的字符串
- 不被引号包围的字符串中出现变量时也会被解析,这一点和双引号" "包围的字符串一样。
- 字符串中不能出现空格,否则空格后边的字符串会作为其他变量或者命令解析。
获取字符串长度
在 Shell 中获取字符串长度很简单,具体方法如下:
string_name 表示字符串名字。
Shell字符串拼接(连接、合并)
在Shell脚本中,字符串拼接(也称为连接或合并)是一项基本操作,用于将两个或多个字符串值组合成一个新的字符串值。Shell(如Bash)提供了多种方法来执行字符串拼接。
虽然双引号(")和单引号(')主要用于定义字符串,但它们并不直接用于拼接字符串。然而,你可以通过将多个字符串变量或字面量放在同一个双引号或单引号中来“模拟”拼接的效果,但这种方式更多是在定义字符串时包含变量或特殊字符。
# 使用双引号拼接字符串
str1="Hello, "
str2="World!"
result="${str1}${str2}" # 推荐的方式
# 或者
result="$str1$str2" # 如果变量之间没有其他字符
echo $result # 输出: Hello, World!
printf命令提供了更灵活的字符串格式化选项,包括字符串拼接。
str1="Hello, "
str2="World!"
printf -v result "%s%s\n" "$str1" "$str2"
echo $result # 输出: Hello, World!
Shell字符串截取
Shell 截取字符串通常有两种方式:从指定位置开始截取和从指定字符(子字符串)开始截取。
从指定位置开始截取
- 这种方式需要两个参数:除了指定起始位置,还需要截取长度,才能最终确定要截取的字符串。
- 既然需要指定起始位置,那么就涉及到计数方向的问题,到底是从字符串左边开始计数,还是从字符串右边开始计数。答案是 Shell同时支持两种计数方式。
从指定字符(子字符串)开始截取
- 这种截取方式无法指定字符串长度,只能从指定字符(子字符串)截取到字符串末尾。Shell可以截取指定字符(子字符串)右边的所有字符,也可以截取左边的所有字符。
从指定位置开始截取
从字符串左边开始计数,如果想从字符串的左边开始计数,那么截取字符串的具体格式如下:
其中,string 是要截取的字符串,start 是起始位置(从左边开始,从 0 开始计数),length 是要截取的长度(省略的话表示直到字符串的末尾)。
从右边开始计数,如果想从字符串的右边开始计数,那么截取字符串的具体格式如下:
从指定字符(子字符串)开始截取
使用 # 号截取右边字符,使用#号可以截取指定字符(或者子字符串)右边的所有字符,具体格式如下:
其中,string 表示要截取的字符,chars 是指定的字符(或者子字符串),*是通配符的一种,表示任意长度的字符串。*chars连起来使用的意思是:忽略左边的所有字符,直到遇见 chars(chars 不会被截取)。
从指定字符(子字符串)开始截取
以下写法也可以得到同样的结果:
如果不需要忽略 chars 左边的字符,那么也可以不写*,例如:
最后一个指定字符(子字符串)再匹配结束,那么可以使用##,具体格式为: