一文了解shell语法(一)

前言:自从操作系统诞生以来,相对应的脚本语言就开始随着发展起来,Shell 是操作系统中用户与内核交互的重要工具,它的发展贯穿了计算机历史的多个阶段,随着操作系统的发展逐渐演变成熟,相较于windows的bat脚本,shell拥有更加强大的高级功能,也可以实现许多bat脚本做不到的功能,本文就着重介绍一下shell脚本语法,以供大家共同学习参考。限于篇幅,分成两个文章来解释


1,发展历程

简单介绍一下,也算是增加一下大家的知识面。。。

起源:Unix 和 Bourne Shell (1970s)

  • 1969 年 - Unix 系统诞生
    Unix 操作系统由贝尔实验室的 Ken Thompson 和 Dennis Ritchie 开发。最早的 Shell 是由 Ken Thompson 开发的 Thompson Shell(sh),它是一个简单的命令解释器,能运行程序,但功能相对有限。

  • 1977 年 - Bourne Shell (sh)
    Steve Bourne 在 Unix V7 系统中引入了 Bourne Shell(/bin/sh)。它成为了第一个功能强大的 Shell,也是现代 Shell 的祖先

扩展:C Shell 和 Korn Shell (1980s)

  • 1978 年 - C Shell (csh)
    由 Bill Joy 开发,C Shell 引入了类似 C 语言的语法和交互式功能,例如:

    • 历史命令(命令回溯与重用)。
    • 别名(alias)。
    • 改进的用户界面交互功能(如 ! 命令历史回溯符号)。
  • 1983 年 - Korn Shell (ksh)
    由 David Korn 开发的 Korn Shell 结合了 Bourne Shell 和 C Shell 的优点:

    • 完全兼容 Bourne Shell 的脚本。
    • 更高效的性能。
    • 增加了函数、数组、正则表达式支持。
    • 支持命令行编辑(如 vi 风格的命令行操作)

GNU 时代:Bash Shell (1989)

  • 1989 年 - GNU Bash (Bourne Again Shell)
    作为 GNU 项目的一部分,Brian Fox 开发了 Bash Shell,旨在替代 Bourne Shell,并增加了许多新特性

现代化:Z Shell 和 Fish Shell (1990s - 至今)

  • 1990 年 - Z Shell (zsh)
    由 Paul Falstad 开发,Z Shell 结合了 Bourne Shell、Korn Shell 和 C Shell 的优点,并引入了现代化的功能

  • 2005 年 - Fish Shell (Friendly Interactive Shell)
    Fish 是专为用户友好性设计的现代 Shell,强调易用性和直观性

专业应用:PowerShell (Windows 系统)

  • 2006 年 - Windows PowerShell
    Windows 引入了 PowerShell,专为管理和脚本编程而设计

最新趋势:Shell 与 DevOps 自动化

  • 随着 DevOps 和云计算的兴起,Shell 的自动化能力被进一步发掘,尤其在构建、部署和管理系统方面。
  • Shell 已逐渐和现代工具(如 Docker、Kubernetes、Ansible 等)结合,通过脚本实现高效的自动化。

2,shell脚本简介

2.1 特点

  • 属于弱类型语言,所以定义变量时不需要指定变量类型
  • Shell中只有字符串一种变量类型,所有数据实际上都是以字符串形式处理,所有变量和数据都可以看作是字符串;
  • 属于解释型语言,编写好的Shell脚本需要指定的shell解释器才能执行

2.2 组成

  • 首行的 shebang 机制,指定需要使用的shell解释器类型
  • 需要执行的相关指令
  • 注释信息

2.3 shell解释器

现在 Linux 操作系统中,一般用的shell解释器都是bash,除了bash还有其他的一些解释器都是可用的
# 查看当前使用的解释器类型
ubuntu@yyy:~$ echo $SHELL
/bin/bash
# 查看当前系统支持的解释器
ubuntu@yyy:~$ cat /etc/shells 
# /etc/shells: valid login shells
/bin/sh
/bin/bash
/usr/bin/bash
/bin/rbash
/usr/bin/rbash
/bin/dash
/usr/bin/dash

1.4 执行脚本

# 方法一:解释器名称 + 脚本路径 ,需要当前用户具有对这个脚本文件的读权限
bash backup.sh
# 方法二:类似于执行命令一样执行脚本,需要当前用户具有对这个脚本文件的读和执行权限;
# 绝对路径
/home/tom/backup.sh

# 相对路径 ./表示在当前目录下
./bash.sh

3,变量

3.1 定义变量

(1)格式: 变量名=变量值
(2)特点:
  • =的两边不可以有任何的空格
  • 变量由数字、字母和下划线组成,且不允许使用数字开头
  • 在变量赋值时,无论是否加引号,赋值时变量的值都不会受到影响

3.2 销毁变量

(1)格式: unset 变量名
(2)例如: unset SERVER_PATH

3.3 调用变量

(1)格式: $变量名 或 ${变量名}
(2)例如:
# 使用大括号的目的是用来将变量部分和后续的字符进行分隔
${SERVER_PATH}
(3)注意点:
  • 当调用变量加上引号的时候
变量值会做为一个整体进行输出,格式会得到保存,包括换行符、空格等符号也不会被处理;
变量值里面的通配符不会被进行解析为文件名
IFS 不会对变量内容进行任何拆分或替换
  • 当调用变量不加上双引号的时候
变量值会根据当前的 IFS(默认为空格、换行符、制表符)将变量的值进行分割。换行符会被转换为空格,多个连续的空格也会被压缩为一个。
如果变量的值中包含通配符(例如 *、? 等),Shell 会将它们扩展为匹配当前目录下的文件名。
ubuntu@yyy:~/test$ ls -1 
file1 
file2 
file3 
file4 
# 定义变量 
ubuntu@yyy:~/test$ NAME=$(ls -1) 
# 输出变量 
ubuntu@yyy:~/test$ echo $NAME 
file1 file2 file3 file4 
# 加引号时输出变量 
ubuntu@yyy:~/test$ echo "$NAME" 
file1 
file2 
file3 
file4

3.4 指定默认变量

(1)临时使用默认值
格式: 变量名:-默认值  ${var:-"default_value"}
说明:如果变量 var 没有设置或者为空,则使用默认值 "default_value",但不会改变
var 的值。如果 var 已经设置且不为空,直接使用 var 的值,后续再次使用的时候就不再是这个设置的值了
#如果 var 没有设置或为空,结果会是默认值 "default_value",但是 var 本身不会被修改
var=""
echo ${var:-"default_value"}  # 输出 "default_value" (因为 var 为空)

# 如果var 已经有值且非空,那么输出var 的值
var="Hello"
echo ${var:-"default_value"}  # 输出 "Hello" (因为 var 已设置且非空)
(2) 改变原有变量的值
格式: 变量名:-默认值  ${var:="default_value"}
说明:如果变量 var 没有设置或者为空,则将 var 设置为 "default_value",并且返回
var 的值。即,它会 修改 var 的值,将其设为默认值
var=""
echo ${var:="default_value"}  # 输出 "default_value" ,并且 var 的值被设为 "default_value"

var="Hello"
echo ${var:="default_value"}  # 输出 "Hello" ,并且 var 的值保持不变

4,数组

4.1 普通数组

(1)格式: arry_name=(value1 value2 value3 value4)
(2)特点:
  • 索引是整数,不能自定义为字符串,并且定义数组时赋值的话,不能手动指定索引,索引是从0开始的连续整数
  • 定义数组时指定数组元素,可以使用空格或换行符来做为元素之间的分隔符
PATTERNS=(
    "lct_net_manager.tb_err_future_loc_20%"
    "lct_net_manager.tb_heartbeat_data_20%"
    "lct_net_manager.tb_loc_data_20%"
    "lct_net_manager.tb_ori_loc_data_20%"
    )

(3)操作普通数组:

# 定义一个数组
fruits=("apple" "banana" "cherry")
# 获取指定索引的值
echo ${fruits[1]} #"banana"
# 修改数组元素
fruits[1]="orange"

# 添加新元素
fruits+=("mango")

# 输出修改后的数组
echo ${fruits[@]}   # 输出 apple orange cherry mango

4.2 关联数组

注意: 关联数组是通过 键(key) 而不是索引来访问元素。这种数组是 Bash 4.0 及以上版本才支持的
(1)格式 declare -A arry_name=([key1]=v1 [key2]=v3 [key3]=v3)
(2)特点:
  • 数组赋值时,可以给数组的元素加上自定义的索引,而不是默认的整数索引
  • 数组赋值的时候需要在方括号 [] 内指定索引(键),否则会报错,不像普通数组一样会自动使用整数作为索引
3)操作数组
# 定义关联数组
declare -A array_name
# 向关联数组添加元素
array_name[key]="value"
# 访问关联数组元素
${array_name[key]}
# 获取所有键(keys)
${!array_name[@]}
# 获取所有值(values)
${array_name[@]}
# 获取数组元素的个数
echo ${#array_name[@]}   # 数组元素的个数
# 清空数组
unset array_name
# 清空单个数组元素
unset array_name[index]

(4)举例

# 定义关联数组
declare -A capitals
# 添加元素
capitals["USA"]="Washington,D.C."
capitals["France"]="Paris"
capitals["Japan"]="Tokyo"
# 访问元素
echo${capitals["USA"]} # 输出 Washington, D.C.
# 获取所有的键
echo${!capitals[@]} # 输出 USA France Japan
# 获取所有的值
echo${capitals[@]} # 输出 Washington, D.C. Paris Tokyo

4.3 数组总结


5, 字符串

注意:Shell只有字符串这一种类型,所有的变量或变量值都可以看作是字符串

5.1 获取变量值长度

格式:len=${#variable}

例子:

ubuntu@yyy:~$ num=123456789

ubuntu@yyy:~$ len=${#num}

ubuntu@yyy:~$ echo $len

5.2 删除变量值的前缀

  • 从字符串开头删除最短匹配:
格式: variable=${variable#pattern}
作用:删除从变量值开头到第一个匹配
【pattern解释】
固定字符串
pattern="file":删除以 file 开头的部分。
pattern="txt":删除以 txt 结尾的部分。
通配符模式
pattern="*file":删除所有以 file 结尾的前缀。
pattern="file*":删除所有以 file 开头的前缀。
pattern="*abc*":删除包含 abc 的任何部分。
多字符通配符
pattern="*abc*":表示删除包含 abc 字符串的部分,不论 abc 在字符串的哪个位置。
  • 从字符串开头删除最长匹配:
格式: variable=${variable##pattern}
作用:删除从变量值开头到最后一个匹配pattern 的部分,直到字符串的末尾。
variable="examplefile.txt"
result=${variable#example}   # 删除最短匹配的前缀 "example"
echo $result   # 输出 "file.txt"

variable="examplefileexample.txt"
result=${variable##example}   # 删除最长匹配的前缀 "examplefile"
echo $result   # 输出 "example.txt"

5.3 删除变量值的后缀

  • 从字符串末尾删除最短匹配
格式: variable=${variable%pattern}
作用:删除从变量值末尾到第一个匹配pattern 的部分
  • 从字符串末尾删除最长匹配
格式: variable=${variable%%pattern}
作用:删除从变量值末尾到最后一个匹配pattern 的部分。
variable="file.txt"
result=${variable%txt}   # 删除最短匹配的后缀 "txt"
echo $result   # 输出 "file."

variable="examplefileexample.txt"
result=${variable%%example.txt}   # 删除最长匹配的后缀 "examplefileexample.txt"
echo $result   # 输出 "file"

 # 你也可以使用通配符(如 * 和 ?
 variable="file_123abc"
result=${variable#*_}   # 删除最短匹配的前缀 "_"
echo $result   # 输出 "123abc"

5.4 检查是否包含特定字符串

格式:[[ string == *substring* ]]

variable="Hello, world!"

if [[ $variable == *"world"* ]]; then
  echo "字符串包含 'world'"
else
  echo "字符串不包含 'world'"
fi

6,函数

6.1 函数的定义

# 格式一
function fun_name()
{
    command;
}	
# 格式二
function fun_name
{
	command;
	command;
}
# 格式三
fun_name() 
{
	command;
	command;
}

6.2 函数的传参

  • 调用函数时,只需要将参数写在函数名后面即可,使用空格进行分隔
  • 函数内部通过Shell预定义的位置变量就可以接收到调用函数时传递的参数
  • 函数可以接受多个参数,使用 $1, $2, ..., $N 来引用这些参数
  • 使用 $@ 或 $* 来引用所有参数
  • $# 可以获取参数的个数
【1 带有默认参数的传参】
#!/bin/bash

greet() {
  local name=${1:-"Guest"}  # 如果未提供参数,使用 "Guest" 作为默认值
  echo "Hello, $name!"
}

greet "Alice"   # 输出: Hello, Alice!
greet           # 输出: Hello, Guest!

【2 函数的普通传参】
#!/bin/bash

print_args() {
  echo "Total arguments: $#"
  echo "Arguments: $@"
}

print_args "arg1" "arg2" "arg3"
# 以下的输出为
# Total arguments: 3
# Arguments: arg1 arg2 arg3

6.3 函数的返回值

  • 函数可以通过 return 语句返回一个退出状态码(0 到 255 之间的整数),表示函数的执行结果
#!/bin/bash

# 定义一个返回整数的函数
add() {
  result=$(( $1 + $2 ))  # 计算两个参数的和
  return $result  # 返回计算结果(0-255之间的整数)
}

add 3 4
echo "The result is $?"  # 使用 $? 获取上一个命令的返回值            
  • 如果你需要返回更复杂的数据(如字符串或浮动值),通常会使用 echo 输出值,而不是 return
#!/bin/bash

# 定义一个返回字符串的函数
greet() {
  echo "Hello, $1!"
}

message=$(greet "Alice")  # 捕获函数的输出
echo "Message: $message"

6.4 函数的局部变量与全局变量

  • 默认情况下,函数内部的变量是 局部的,只在函数内有效
  • 使用 local 关键字可以显式地声明局部变量,防止与外部变量冲突
#!/bin/bash

greet() {
  local name=$1  # 局部变量
  echo "Hello, $name!"
}

name="Global"
greet "Alice"    # 输出: Hello, Alice!
echo $name       # 输出: Global(函数内的局部变量不影响外部变量)

5.5 函数的终止

  • 使用 exit 语句可以在函数内终止脚本的执行(并返回一个退出状态码)
#!/bin/bash

exit_function() {
  echo "This is an exit function"
  exit 1  # 退出脚本并返回状态码 1
}

exit_function
echo "This line will not be executed"  # 不会被执行

7,算数运算

Shell中虽然只有字符串一种数据类型,但是可以使用数字型字符串(仅包含数字字符0-9)来表示和操作数字,然后通过相应的工具和命令来实现进行算数运算

7.1 表达式

  • 表达式:编程中的表达式由常量、变量、操作符和函数等组成的代码片段,可以通过计算产生一个值;
  • 运算表达式:只包含数字和算术运算符,不包含赋值符号 =,用于执行数学运算。
  • 赋值表达式:包含赋值操作符 =的表达式,用于将一个值(或另一个表达式的结果)赋给变量。
  • 操作符表达式:包含算术运算符、逻辑运算符、比较运算符等,但不包含赋值运算符 =。主要作用是进行计算、判断条件以及执行逻辑操作,而不涉及赋值操作

7.2 算数运算命令

#############################
# let命令
#############################
#!/bin/bash

a=5
b=3
let sum=a+b   # 计算 a + b
echo "Sum: $sum"  # 输出 8

let a++  # 自增
echo "After increment: $a"  # 输出 6

let a+=10  # 增加 10
echo "After addition: $a"  # 输出 16

#############################
# expr命令
# expr 用于执行算术运算,但变量名前必须加$,且需要将运算符用空格分隔
#############################
#!/bin/bash

a=5
b=3
sum=$(expr $a + $b)  # 使用 expr 计算 a + b
echo "Sum: $sum"  # 输出 8

product=$(expr $a \* $b)  # 乘法需要使用 \* 转义
echo "Product: $product"  # 输出 15

difference=$(expr $a - $b)  # 减法
echo "Difference: $difference"  # 输出 2

#############################
# bc命令
# bc 用于执行精确的数学运算,包括浮点数运算。它可以通过管道传递表达式
#############################
#!/bin/bash

a=5
b=3
sum=$(echo "$a + $b" | bc)  # 使用 bc 计算 a + b
echo "Sum: $sum"  # 输出 8

division=$(echo "scale=2; $a / $b" | bc)  # 保留两位小数
echo "Division: $division"  # 输出 1.66

sqrt=$(echo "scale=2; sqrt(25)" | bc)  # 求平方根
echo "Square root: $sqrt"  # 输出 5.00

#############################
# (( ))命令
# (( )) 是 Bash 内建的算术扩展,支持变量直接使用,无需加 $,返回退出状态码
#############################
#!/bin/bash

a=5
b=3
(( sum = a + b ))  # 使用 (( )) 计算 a + b
echo "Sum: $sum"  # 输出 8

(( a++ ))  # 自增
echo "After increment: $a"  # 输出 6

if (( a > b )); then  # 支持条件判断
  echo "a is greater than b"
else
  echo "a is less than or equal to b"
fi

 #############################
# $(( ))命令
# $(( )) 是另一种 Bash 内建的算术扩展,结果可以直接用于赋值或输出
#############################
#!/bin/bash

a=5
b=3
sum=$(( a + b ))  # 直接计算 a + b 并赋值
echo "Sum: $sum"  # 输出 8

product=$(( a * b ))  # 乘法
echo "Product: $product"  # 输出 15

difference=$(( a - b ))  # 减法
echo "Difference: $difference"  # 输出 2

remainder=$(( a % b ))  # 求余
echo "Remainder: $remainder"  # 输出 2
               

8,逻辑运算

  • Shell中,没有直接的布尔值类型,不像编程语言那样有 true 和 false 作为专门的布尔数据类型。逻辑判断依赖于命令的退出状态码,即0 表示真非 0 表示假
  • Shell中,通过 $? 可以获取到上一个命令执行后的命令退出状态码;

8.1 与运算(&&)

#!/bin/bash

a=5
b=3

if (( a > 2 && b > 2 )); then
  echo "Both a and b are greater than 2"
else
  echo "At least one of a or b is not greater than 2"
fi

8.2 或运算(||)

#!/bin/bash

a=5
b=1

if (( a > 4 || b > 4 )); then
  echo "At least one of a or b is greater than 4"
else
  echo "Neither a nor b is greater than 4"
fi

8.3 非运算(!)

#!/bin/bash

a=5

if ! (( a > 10 )); then
  echo "a is not greater than 10"
else
  echo "a is greater than 10"
fi

8.4 [[ ]] 中使用逻辑运算符

  • [[ ]] 提供了更强大的条件判断功能,可以支持字符串比较和正则匹配
【1,字符串比较】
#!/bin/bash

str1="hello"
str2="world"

if [[ -n $str1 && $str2 == "world" ]]; then
  echo "str1 is not empty and str2 equals 'world'"
else
  echo "Condition not met"
fi
【2,文件判断】
#!/bin/bash

file="test.txt"

if [[ -e $file && ! -d $file ]]; then
  echo "$file exists and is not a directory"
else
  echo "$file does not exist or is a directory"
fi
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

是星凡呢

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值