Bash(Bourne Again SHell)是一种广泛使用的Unix shell和命令语言,它提供了一套强大的功能用于脚本编写和自动化任务。
1.编写脚本方式和运行脚本方式
sudo vi 名称.sh
- 例如编写 一个名称为a的脚本:
运行方式1:先给权限再运行
sudo chmod +x 文件名
./文件名
-
例如创建一个a.sh的脚本并给他执行的权限
(1)在终端输入下面的代码后enter
sudo vi a.sh
(2)点击 i 进入插入模式,输入以下内容
(3)按esc键退出插入模式,输入“: wq” 保存并退出
(4) 使用命令ll 查看发现a.sh文件没有执行权限
(5)使用下面的命令给a.sh文件加执行权限
sudo chmod +x a.sh
结果如下:
(6)使用./a.sh就可以执行文件
运行方式2:直接 bash 文件名
(1)先将可执行脚本文件去除执行权限
(2)使用 bash ./a.sh
运行方式3:直接 sh 文件名
结果如下:
2. 脚本开头
每个 Bash 脚本都应该以一个 “shebang” 开头,后跟解释器的路径。这告诉系统应该使用哪个解释器来执行这个脚本。
#!/bin/bash
3. 变量
(1)在 Bash 脚本中,变量不需要声明,但赋值时不能在变量名和值之间加空格。
a="Hello, World!"
如下图的变量b:
运行结果如下:
(2)要引用变量,需要在变量名前加上美元符号$
。
echo $a
或者用花括号来明确变量的边界:
echo ${a}
(3)可以将变量设置为只读,这意味着该变量的值不能更改。(类似Java中的常量)
variable="Hello, World!"
readonly variable
# 下面的赋值会失败
variable="Goodbye, World!" # 报错:variable 只读变量
运行结果如下:
补充:也不能使用unset
运行结果如下:
(4)环境变量是全局变量,它们会影响当前shell及其子shell的执行环境。
export 变量名="路径"
1.首先我们要到/etc/profile文件中添加全局变量
sudo vim /etc/profile
2.进入之后光标移动到最底部点击 i 进入插入模式,添加环境变量(例如:MY_NAME="/home/tom")
export MY_NAME=/home/tom
3.输入后,按esc键再输入“: wq”保存并退出
4.输入source /etc/profile 当你执行 source /etc/profile
命令时,你实际上是在告诉shell:“现在,我想重新读取并执行 /etc/profile
文件中的所有命令,以便更新我的环境变量和其他设置。”
source /etc/profile
5.编写一个Shell脚本
运行结果如下:
(5)bash提供了一些特殊变量,例如:
$0
- 脚本名称$1
- 脚本的第一个参数$2
- 脚本的第二个参数$#
- 传递给脚本的参数个数(${#变量名} - 变量的长度)$*
- 所有位置参数的单个字符串$@
- 所有位置参数的单独引用字符串$?
- 最后一条命令的退出状态$$
- 当前shell进程ID(PID)$!
- 最后一个后台命令的进程ID
运行下面代码的注意要传两个参数:(这里我的脚本名字是a,传的参数是10和20)
./a.sh 10 20
#!/bin/bash
echo "Script name: $0"
echo "First argument: $1"
echo "Second argument: $2"
echo "Total number of arguments: $#"
echo "All arguments as a single string: $*"
echo "All arguments as separate strings: $@"
echo "Exit status of last command: $?"
echo "Process ID of current shell: $$"
4. 注释
注释以#
开头,用于解释脚本中的代码。
# 这是一个注释
5. 输入输出重定向
可以使用>
和<
符号来重定向输入输出。
echo "This will be saved in a file." > output.txt
cat < input.txt
6.数学运算
(1)使用 $((...))
进行算术
#!/bin/bash
# 定义变量
a=5
b=3
# 加法
sum=$((a + b))
echo "Sum: $sum"
# 减法
diff=$((a - b))
echo "Difference: $diff"
# 乘法
product=$((a * b))
echo "Product: $product"
# 除法(整数除法)
quotient=$((a / b))
echo "Quotient: $quotient"
# 取余数
remainder=$((a % b))
echo "Remainder: $remainder"
如果你想要动态输入变量将变量a换成$1,将变量b换成$2即可
#!/bin/bash
# 加法
sum=$(($1 + $2))
echo "Sum: $sum"
(2)使用 $[...]
进行算术
#!/bin/bash
# 定义变量
a=10
b=3
# 加法
sum=$[a + b]
echo "Sum: $sum" # 输出: Sum: 13
# 减法
diff=$[a - b]
echo "Difference: $diff" # 输出: Difference: 7
# 乘法
product=$[a * b]
echo "Product: $product" # 输出: Product: 30
# 除法(整数除法)
quotient=$[a / b]
echo "Quotient: $quotient" # 输出: Quotient: 3
# 取余数
remainder=$[a % b]
echo "Remainder: $remainder" # 输出: Remainder: 1
# 使用赋值运算符进行自增
a=$[a + 1]
echo "a after increment: $a" # 输出: a after increment: 11
# 使用赋值运算符进行自减
b=$[b - 1]
echo "b after decrement: $b" # 输出: b after decrement: 2
(3)let
命令可以在Bash中执行算术运算,并更新变量的值。
#!/bin/bash
# 定义变量
a=5
b=3
# 使用let进行加法
let sum=a+b
echo "Sum: $sum"
# 使用let进行减法
let diff=a-b
echo "Difference: $diff"
# 使用let进行乘法
let product=a*b
echo "Product: $product"
# 使用let进行除法(整数除法)
let quotient=a/b
echo "Quotient: $quotient"
# 使用let进行取余数
let remainder=a%b
echo "Remainder: $remainder"
7. 控制流
(1)单分支if 语句
语法格式:
if 条件测试表达式;then
操作语句(可以多条)
fi
1.[ ]命令类型
#!/bin/bash
number=10
if [ $number -gt 5 ]; then
echo "Number is greater than 5."
fi
在这个例子中,我们定义了一个变量 number
并赋值为 10。if
语句检查 number
是否大于 5。如果条件为真,则执行 then
后面的代码块,输出 “Number is greater than 5.”。
2. test
命令类型
test
命令可以用来执行条件测试,它与 []
的用法基本相同,但是不需要括号。
#!/bin/bash
file="test.txt"
if test -e "$file"; then
echo "File exists."
else
echo "File does not exist."
fi
3. 双中括号 [[ ]]类型
双中括号 [[ ]]
提供了更强大的模式匹配功能,并且允许使用正则表达式。它通常用于更复杂的条件测试。
#!/bin/bash
string="Hello World"
if [[ $string == *"World"* ]]; then
echo "The string contains 'World'."
fi
在 [[ ]]
中,可以使用 ==
来比较字符串,并且可以使用通配符,如 *
。
4. 双括号 (( ))类型
双括号 (( ))
用于执行算术比较和赋值。它允许在比较中使用高级数学表达式。
#!/bin/bash
num=10
if (( num > 5 )); then
echo "The number is greater than 5."
fi
# 在双括号内也可以进行赋值和算术运算
(( num++ ))
echo "The number is now $num."
在 (( ))
中,不需要在变量名周围使用引号,也不需要在操作符周围加空格。
注意事项
[
和]
是测试命令的符号,与test
命令等价。[
命令和要测试的变量或表达式之间必须有空格。- 在使用
[
命令时,变量不需要使用$
符号。 - 在使用
[[
双方括号时,支持更多的模式和操作,并且不需要空格分隔。
表达式类型 | 语法 | 支持的操作符 | 特点与限制 | 整数比较符 | 字符串比较操作符 |
test 命令 | test expression | 文件测试(如 -e , -d )、字符串比较(如 = , != )、整数比较(如 -eq , -lt ) | 传统的条件测试命令,返回退出状态(0为真,非0为假) | -eq (等于)、-ne (不等于)、-lt (小于)、-le (小于等于)、-gt (大于)、-ge (大于等于) | =、==、!= |
[] (测试表达式) | [ expression ] | 与 test 命令相同 | test 命令的简写形式,需要注意特殊字符的转义 | -eq (等于)、-ne (不等于)、-lt (小于)、-le (小于等于)、-gt (大于)、-ge (大于等于) | =、==、!= |
[[]] (扩展的测试表达式) | [[ expression ]] | 支持字符串模式匹配(如 * , ? )、逻辑运算符(如 && , ||) | [[]] 提供了比 [] 更强大的测试功能,包括支持字符串模式匹配(使用 * 和 ? )、逻辑运算符 && 和 || 无需转义、以及更复杂的表达式评估。 | -eq (等于)、-ne (不等于)、-lt (小于)、-le (小于等于)、-gt (大于)、-ge (大于等于)< (小于)、<= (小于等于)、> (大于)、>= (大于等于)、== (等于)、!= (不等于) | =、==、!= |
(()) (算术表达式) | (( expression )) | 算术运算符(如 + , - , * , / , % )、整数比较(如 < , > , <= , >= ) | 用于执行算术计算和比较,返回算术结果(可用于条件语句中的算术比较) | < (小于)、<= (小于等于)、> (大于)、>= (大于等于)、== (等于)、!= (不等于) | =、==、!= |
(2)多分支if-elif-else
语句
语法格式:
if 条件测试表达式;then
操作语句1(可以多条)
elif 条件测试表达式;then
操作语句2(可以多条)
else
操作语句3(可以多条)
fi
#!/bin/bash
number=7
if [ $number -gt 10 ]; then
echo "Number is greater than 10."
elif [ $number -gt 5 ]; then
echo "Number is greater than 5 but not greater than 10."
else
echo "Number is not greater than 5."
fi
这里我们使用了 elif
(else if的简写),它允许我们检查多个条件。如果第一个条件失败,它会检查第二个条件,如果第二个条件也失败,则执行 else
代码块。
(3)for 循环
1. 基本的 for
循环
在 Bash 中,最基本的 for
循环可以遍历一系列的值。
for 变量 in {list}
do
操作语句(可以多条)
done
for循环执行次数和 list列表中常数或字符串的个数相同,先将in后面list列表的第一个常数或字符串赋值给变量,然后执行循环体;接着将list列表第二个赋值给变量,再次执行循环体。这个过程持续到list列表执行完,然后执行done后的操作命令。
#!/bin/bash
# 遍历列表中的每个元素
for fruit in apple banana cherry
do
echo "I like $fruit"
done
在这个例子中,for
循环遍历了列表 apple banana cherry
中的每个元素,并在每次迭代中将当前的元素赋值给变量 fruit
,然后执行 echo
命令。
结果如下:
2. 使用通配符
for
循环也可以使用通配符来遍历文件。
#!/bin/bash
# 遍历当前目录下所有的 .txt 文件
for file in *.txt
do
echo "Processing $file"
done
这个例子中,for
循环会遍历当前目录下所有扩展名为 .txt
的文件。
结果如下:
3. 使用序列
你可以使用 {start..end}
语法生成一个序列,并使用 for
循环遍历它。
#!/bin/bash
# 使用序列从 1 遍历到 5
for i in {1..5}
do
echo "Number: $i"
done
这个例子中,for
循环会打印数字 1 到 5。
结果如下:
如果想遍历1-5内的所有奇数,只需将{1..5}改成{1..5..2}
#!/bin/bash
# 使用序列从 1 遍历到 5
for i in {1..5..2}
do
echo "Number: $i"
done
结果如下:
4. 使用 seq
命令
seq
命令可以生成一个数字序列,常与 for
循环结合使用。
#!/bin/bash
# 使用 seq 命令从 1 到 5,步长为 1
for i in $(seq 1 5)
do
echo "Count: $i"
done
这个例子中,seq 1 5
生成一个从 1 到 5 的序列,for
循环遍历这个序列。
结果如下:
5. C 风格的 for
循环
Bash 也支持类似于 C 语言的 for
循环语法。
#!/bin/bash
# C 风格的 for 循环
for (( i=0; i<5; i++ ))
do
echo "Index: $i"
done
这个例子中,for
循环使用类似于 C 语言的控制变量 i
,初始化为 0,并在每次迭代后递增,直到 i
小于 5 为止。
结果如下:
(4)while 循环
while
循环是一种控制结构,它重复执行一组命令,直到给定的条件不再为真。
1. 基本的 while
循环
这是一个基本的 while
循环,它将一直执行,直到条件变为假。
while 条件测试表达式
do
操作语句(可以多条)
done
#!/bin/bash
# 初始化计数器
count=1
# while 循环,直到 count 大于 5
while [ $count -le 5 ]
do
echo "Count: $count"
# 增加计数器的值
((count++))
done
在这个例子中,while
循环会一直执行,直到变量 count
的值大于 5。每次循环,它都会打印当前的 count
值,并将其递增。
结果如下:
2. 读取文件行
while
循环常用于读取文件中的每一行。
#!/bin/bash
# 读取文件中的每一行
while read line
do
echo "$line"
done < "filename.txt"
这个例子中,while
循环使用 read
命令从文件 “filename.txt” 中读取每一行,并打印它。循环会继续,直到文件中没有更多的行可以读取。
-
例如我有一个study.txt 文件,所以我执行下面的内容
#!/bin/bash
# 读取文件中的每一行
while read line
do
echo "$line"
done < "study.txt"
结果如下:
3. 无限循环
如果你不提供退出条件,while
循环将变成一个无限循环。
#!/bin/bash
# 无限循环
while true
do
echo "Looping forever..."
sleep 1 # 等待 1 秒
done
这个例子中,while
循环会无限地执行,每次循环打印消息并暂停一秒钟。要停止这个循环,你需要手动中断脚本(例如,按 Ctrl+C)。
结果如下:
4. 使用控制变量
在 while
循环中,你可以使用一个控制变量来决定何时退出循环。
#!/bin/bash
# 控制变量
number=1
# while 循环,直到 number 大于 5
while [ $number -le 5 ]
do
echo "Number: $number"
((number++))
# 如果 number 等于 4,则退出循环
if [ $number -eq 4 ]; then
break
fi
done
在这个例子中,while
循环在 number
达到 4 时通过 break
命令提前退出。
结果如下:
8.函数
在 Bash 脚本中,函数是一段可重用的代码块,它可以接受参数,执行一系列命令,并可能返回一个值。
1. 定义和调用一个简单的函数
下面是一个定义和调用简单函数的例子:
#!/bin/bash
# 定义函数 greet
greet() {
echo "Hello, $1!"
}
# 调用函数 greet 并传递参数 "World"
greet "World"
或者
#!/bin/bash
# 定义函数 greet
function greet() {
echo "Hello, $1!"
}
# 调用函数 greet 并传递参数 "World"
greet "World"
在这个例子中,greet
是一个接受一个参数的函数。当你调用 greet "World"
时,它会在屏幕上打印 “Hello, World!”。
结果如下:
如果你想要在终端上输入你想要的内容可以加上read 命令
#!/bin/bash
# 定义函数 greet
function greet() {
echo "Hello,$context !"
}
# 调用函数 greet 并传递参数 "World"
read -p "请输入内容:" context
greet $context
结果如下:
2. 使用局部变量
默认情况下,Bash 函数中的变量是全局的。如果你想要在函数内部使用局部变量,可以使用 local
关键字。
#!/bin/bash
# 定义函数 greet_with_local
greet_with_local() {
local name=$1
echo "Hello, $name!"
}
# 调用函数 greet_with_local
greet_with_local "Alice"
在这个例子中,name
是一个局部变量,它只在 greet_with_local
函数内部有效。
3. 函数返回值
Bash 函数可以通过 return
语句返回一个整数,或者通过 echo 来返回字符串。
#!/bin/bash
# 定义函数 add,返回两个数的和
add() {
local a=$1
local b=$2
local sum=$((a + b))
echo $sum
# 或者使用 return 语句返回整数结果
# return $sum
}
# 调用函数 add 并获取返回值
result=$(add 5 3)
echo "The result is $result"
在这个例子中,add
函数接受两个参数,计算它们的和,并通过 echo
返回结果。调用函数时,可以使用 $()
来捕获函数的输出。
4. 函数中使用位置参数
Bash 函数可以像脚本一样使用位置参数 $1
, $2
, $3
等。
#!/bin/bash
# 定义函数 print_params
print_params() {
echo "Parameter 1: $1"
echo "Parameter 2: $2"
echo "All parameters: $*"
}
# 调用函数 print_params 并传递参数
print_params "First" "Second" "Third"
在这个例子中,print_params
函数打印了传递给它的每个参数,以及所有参数的列表。
9.数组
在 Bash 中,数组是一组数据的集合,可以存储一个或多个元素。数组元素可以是任何数据类型,但通常使用字符串和整数。
(1)数组的定义
在Shell脚本中,数组可以通过以下两种方式之一来定义:
1.显式定义:使用括号()
和空格分隔的元素来定义数组。
# 定义一个包含三个元素的数组
my_array=(element1 element2 element3)
2.隐式定义:通过赋值给数组的索引来定义数组。
# 逐个定义数组元素
my_array[0]=element1
my_array[1]=element2
my_array[2]=element3
(2)访问数组元素
数组元素可以通过索引来访问,索引从0开始。
# 访问第一个元素
echo ${my_array[0]} # 输出: element1
# 访问第二个元素
echo ${my_array[1]} # 输出: element2
(3)获取数组长度
可以使用特殊变量${#array_name[@]}
或${#array_name[*]}
来获取数组的长度(即元素的个数)。
# 获取数组长度
array_length=${#my_array[@]}
echo $array_length # 输出: 3
(4)遍历数组
可以使用for
循环来遍历数组元素。
# 使用for循环遍历数组
for element in "${my_array[@]}"; do
echo $element
done
或者,使用数组的索引来遍历:
# 使用索引遍历数组
for i in "${!my_array[@]}"; do
echo "Element at index $i: ${my_array[$i]}"
done
注意事项
- Shell脚本中的数组是一维的,不支持多维数组。
- 数组索引是从0开始的整数。
- 在访问数组元素时,建议使用双引号将变量名括起来(如
${my_array[0]}
),以避免在元素包含空格或特殊字符时出现问题。 - 在某些Shell(如Dash,它是Debian和Ubuntu的
/bin/sh
的默认Shell)中,数组功能可能受到限制或不完全支持。因此,在编写依赖于数组功能的脚本时,最好指定使用Bash(或其他支持数组的Shell)。