shell脚本_永久环境变量和字符串操作和shell如何变成永久变量-CSDN博客
shell脚本_脚本与用户交互以及if条件判断-CSDN博客
shell脚本_if条件判断与for循环结构讲解与示例-CSDN博客
shell脚本_for循环与while循环讲解和对比-CSDN博客
shell脚本_until循环和case语句与函数的定义和使用-CSDN博客
shell脚本_不同脚本的互相调用和重定向操作-CSDN博客
学习视频泷羽sec:
泷羽sec的个人空间-泷羽sec个人主页-哔哩哔哩视频https://space.bilibili.com/350329294
导语
在这篇总结篇中,我们将回顾和总结前面学习的 Shell 脚本基础知识。Shell 脚本是系统管理、自动化任务执行和日常操作中不可或缺的一部分,掌握它将帮助我们提高工作效率和系统操作的灵活性。从最基础的变量定义和条件判断,到复杂的循环结构和函数使用,Shell 脚本提供了强大而灵活的工具,让我们能够快速完成任务。在本篇文章中,我们将对这些内容进行系统的总结,帮助大家在实际工作中更好地运用 Shell 脚本。
一、shell脚本简介
Shell脚本是一个由一系列Shell命令、控制结构和逻辑语句组成的脚本文件,用于在类Unix操作系统中自动化执行一系列任务。它通过Shell(如Bash、Zsh、Sh等)解释执行,通常用于系统管理、文件操作、批处理任务以及自动化工作流等场景。
1. 定义:
Shell脚本是一个包含多个命令和程序逻辑的文本文件,通过Shell(命令行解释器)解释执行,旨在自动化或简化日常任务。
2. 主要特点:
-
命令自动化:Shell脚本可以将多个Shell命令组合成一个自动化脚本,避免手动输入命令。
-
文本文件:脚本本质上是一个文本文件,可以使用任何文本编辑器(如Vim、Nano、VS Code等)编辑。
-
解释执行:Shell脚本并不需要事先编译,它由Shell解释器直接执行,通常通过命令行终端运行。
-
系统管理与自动化:常用于自动化系统管理任务(如备份、文件处理、日志分析等)以及批处理操作。
3. shell脚本的基本结构
-
Shebang:指定解释器。
-
注释:描述脚本功能或解释代码。
-
命令:实现脚本功能的核心部分。
-
变量:存储和操作数据。
-
控制结构:如条件判断和循环,控制程序流。
-
函数:组织代码,提升可复用性。
-
输入输出:与用户交互并处理数据。
-
退出状态:通过状态码反馈执行结果。
4. Shebang
Shebang是Shell脚本的第一行,用于指定脚本应由哪个解释器执行。在Linux和Unix系统中,Shebang通常以 #!
开头,后面跟着解释器的路径。
-
示例:#!/bin/bash
这行指示系统使用
/bin/bash
作为解释器来执行这个脚本。 -
常见Shebang:
-
#!/bin/bash
:使用Bash解释器(最常见) -
#!/bin/sh
:使用系统默认Shell(通常是Bash或Dash) -
#!/usr/bin/env python3
:用于Python脚本,指定Python解释器。
-
二、创建执行
1.脚本的创建
-
可以用touch或者echo命令来创建sh文件
-
可以使用任何文本编辑器(如
nano
,vi
,vim
,gedit
,emacs
等)来创建脚本文件。
2. 脚本的执行
在默认情况下我们创建的脚本是没有权限被执行的。如果不明白看往期章节关于文件的权限部分Linux基础-常用操作命令详讲
这里有两种方案
2.1.chmod
Shell脚本文件本身需要具有“可执行”权限。使用 chmod
命令,你可以赋予脚本文件适当的权限,使得它能够被系统执行。
-
chmod +x script.sh
-
+x
表示给文件添加“执行”权限。 -
script.sh
是你的Shell脚本文件名。
-
2.2. 使用脚本解释器直接运行脚本
如果你的脚本没有执行权限,但你知道它是有效的Shell脚本,你可以直接通过指定解释器,通过它们可以运行脚本内容,无论脚本本身是否有执行权限。
-
/bin/bash script.sh # 使用 Bash 解释器执行脚本
-
/bin/sh script.sh # 使用默认的 Bourne shell 执行脚本
-
/bin/dash script.sh # 使用 Dash shell 执行脚本
这三种其实不管用哪种脚本解释器最后调用的还是bash。
2.3.source
-
source
命令会读取并执行一个文件中的内容,所有在该脚本中定义的变量、函数、或者更改的环境设置都会保留在当前 shell 中。 -
source
可以用来重新加载配置文件,或在当前 shell 中运行脚本,保持环境的连续性。 -
它和sh的区别是存在颜色加深,会显示如原本命令的目录颜色、文件颜色。
-
source script.sh
3. 解释器的作用
在Linux和类Unix系统中,解释器(Interpreter)是一个将脚本文件(例如Shell脚本)中的代码逐行翻译并执行的程序。 /bin/bash
、/bin/sh
和 /bin/dash
都是不同的Shell解释器。
解释器的作用是将脚本中的命令逐行解析并执行,因此即使脚本文件本身没有执行权限,只要你指定了合适的解释器,它就能执行。
-
解释器:负责逐行解析并执行脚本文件中的命令。常见的解释器有
/bin/bash
、/bin/sh
和/bin/dash
。 -
直接执行脚本:通过指定解释器(即使文件没有执行权限),你可以运行脚本。系统会通过该解释器来执行脚本内容。
-
/bin/sh
和/bin/dash
:/bin/sh
是一个符号链接,可能指向 Dash 或其他Shell,这决定了脚本的执行环境。
三、变量
在 Shell 脚本中,变量的定义与使用非常简单,但需要注意 Shell 的语法规则。
1. 定义变量
Shell 中定义变量时,变量名和等号之间不能有空格。例如:
variable_name=value
注意:
-
变量名只能由字母、数字和下划线组成,且不能以数字开头。
-
变量定义时不需要使用
=
以外的符号。
2. 引用变量
在使用变量时,需要通过 $
符号来引用它的值。例如:
echo $name
echo $age
如果需要在变量引用中包含更多文本或者字符,可以使用花括号 {}
来确保正确的解析。例如:
echo "My name is ${name} and I am ${age} years old."
PS:多变量必须由双引号包裹或不包裹,变量名字写在${}内,单引号就原模原样输出字符串不读取变量。
3. set 命令查看变量
在 Unix-like 系统(如 Linux 或 macOS)中,set
是一个 shell 内置命令,用于列出所有当前 shell 环境中的变量,包括环境变量和局部变量。
-
列出所有变量:
set
命令将显示当前 shell 环境中的所有变量,包括环境变量、局部变量和 shell 特殊变量。 -
显示的变量包括:
-
环境变量(通过
export
设置的变量) -
局部变量(没有使用
export
设置的变量) -
shell 特殊变量(如
$?
、\$0
等)
-
如果你只输入 set
命令,它将列出当前 shell 环境中的所有变量:
set
输出会非常长,包含许多信息。例如,你可能会看到如下内容包含我们刚才创建的变量:
BASH=/bin/bash
BASH_VERSION='5.0.17(1)-release'
...
MY_VAR='Hello'
4. 通过grep来筛选内容
grep是一个常见的命令,用于查找和筛选包含特定字符串
set | grep name
命令解释
-
set
:该命令列出当前 shell 环境中所有的变量,包括环境变量和局部变量。输出的内容会非常多,包括变量名和它们的值。 -
|
(管道符):这个符号表示将set
命令的输出传递给下一个命令。在这里,它将set
输出的结果传递给grep
命令进行筛选。 -
grep name
:grep
是一个常用的文本搜索工具,它会在输入的内容中查找包含 "name" 的行,并输出匹配的结果。
就会输出我们刚才定义的name和其他包含name的变量
5.删除变量
在 Shell 中,unset
命令用于 删除变量 或 取消函数定义。当你使用 unset
删除一个变量时,这个变量将不再在当前 shell 会话中有效,无法再访问它。
unset name
其中 name
是你想要删除的变量的名称。
-
unset
删除的是当前 shell 环境中的 局部变量 或 环境变量,它不会影响系统或父进程中的其他变量。 -
一旦变量被
unset
删除,你将无法再访问它,直到它被重新定义。
6. 永久环境变量
环境变量是操作系统用来存储系统和应用程序配置信息的键值对。在 Shell 脚本中,环境变量的设置通常用于配置系统或应用程序的运行时环境。而永久环境变量是指那些在用户每次登录时都能自动加载的环境变量。
6. 1. 常见的环境变量
在 Linux 或 Unix 系统中,永久环境变量是那些在系统启动时或用户登录时自动加载,并在会话期间保持有效的环境变量。以下是一些常见的永久环境变量,它们在系统中用来配置用户环境和程序行为。
-
1.
PATH
-
作用:定义了操作系统搜索可执行文件的目录路径。当你在命令行输入命令时,Shell 会按照
PATH
中列出的目录顺序查找命令。 -
常见配置:通过设置
PATH
环境变量,可以将用户自定义的程序目录添加到搜索路径中。
-
export PATH=$PATH:/usr/local/bin:/home/user/my_programs #设置
echo $PATH
-
2.
HOME
-
作用:表示当前用户的主目录。在大多数 Linux 系统中,每个用户都有一个独立的主目录,用于存放个人文件和配置。
echo $HOME # 输出当前用户的家目录路径
-
-
3.
USER
-
作用:表示当前登录的用户名。这个变量会自动由系统设置,并用于指示当前会话的用户。
echo $USER # 输出当前登录的用户名
-
-
4.
SHELL
-
作用:指定当前用户使用的 Shell 类型。例如,对于使用 Bash 的用户,
SHELL
的值通常是/bin/bash
。echo $SHELL # 输出当前使用的 Shell,例如 /bin/bash
-
6.2. 设置永久环境变量
要设置永久环境变量,通常有以下几种方法:
6.2.1.将脚本加进PATH变量的目录中
-
echo $PATH
-
mv script.sh /usr/bin/
-
将预先准备的脚本赋予权限移动到PATH变量的目录中,新打开的shell窗口就可以输入该文件名执行脚本了。
6.2.2.添加进环境变量里
通过将 /root/
目录添加到环境变量 PATH
中,使得该目录下的所有可执行文件都可以直接在命令行中执行。
export PATH=/root:$PATH
1.sh
-
export PATH=/root:$PATH
:这行命令将/root
目录添加到当前PATH
环境变量的前面(注意$PATH
在这里是当前环境变量的值)。-
/root
是你想要添加的目录。 -
$PATH
是现有的PATH
环境变量,包含了其他已有的目录路径。
这样,当你执行一个命令时,系统会首先在
/root
目录中查找可执行文件。如果该目录下有你需要的可执行文件,就会优先执行该文件。 -
6.2.2.修改用户的 shell 配置文件
-
对于 Bash 用户,一般修改
~/.bashrc
(对于交互式非登录 shell)或者~/.bash_profile
(对于登录 shell)。 -
对于 Zsh 用户,通常修改
~/.zshrc
文件。
/usr/bin/vim /root/.bashrc
export PATH=/root:$PATH
esc-->:-->wq(退出)
source /root/.bashrc
-
/usr/bin/vim /root/.bashrc
:使用vim
编辑器打开 root 用户的.bashrc
配置文件。 -
export PATH=/root:$PATH
:将/root
目录添加到PATH
环境变量的最前面。 -
esc --> :wq
:在vim
中按Esc
退出编辑模式,输入:wq
保存并退出。 -
source /root/.bashrc
:使.bashrc
的修改立即生效。 -
1.sh
:执行名为1.sh
的脚本文件(假设该脚本已设置可执行权限)。
PS:删除用rm或者编辑的方式,删除变量
四、字符串操作
在编程中,字符串操作是非常常见且重要的任务之一。无论是在文本处理、文件解析,还是在网络数据处理等场景中,我们都需要灵活地操作和处理字符串。尤其在 Shell 脚本 中,字符串操作不仅是脚本功能的基础,而且直接影响脚本的可读性和效率。
1. 字符串拼接
在 Shell 中,字符串拼接非常简单,可以直接通过空格来连接字符串:
str1="Hello"
str2="World"
result="$str1 $str2" # 拼接后 "Hello World"
echo "$result"
2.字符串切片
在 Shell 中,字符串切片使用 ${string:start:length}
语法:
text="Hello, World!"
slice="${text:0:5}" # 获取 "Hello"
echo "$slice"
如果你想从某个位置切到结尾,可以这样做:
slice="${text:7}" # 从位置 7 开始,获取 "World!"
echo "$slice"
3. 字符串查找
在 Shell 中,可以使用 grep
或 [[ ]]
来判断子字符串是否存在:
-
使用
[[ ]]
判断子字符串是否存在:text="Hello, World!" if [[ "$text" == *"World"* ]]; then echo "Found 'World'" fi
-
使用
grep
查找字符串:text="Hello, World!" echo "$text" | grep -q "World" # 如果找到 "World",返回 0 if [[ $? -eq 0 ]]; then echo "Found 'World'" fi
4. 字符串替换
Shell 也可以进行简单的字符串替换:
-
替换第一次出现的子字符串:
text="Hello, World!" result="${text/World/Python}" # 替换第一个 "World" 为 "Python" echo "$result" # 输出 "Hello, Python!"
-
替换所有出现的子字符串:
text="Hello, World! Hello again!" result="${text//Hello/Hi}" # 替换所有 "Hello" 为 "Hi" echo "$result" # 输出 "Hi, World! Hi again!"
5. 字符串大小写转换
Shell 原生不直接支持大小写转换,但可以借助 tr
或 awk
等工具来实现:
-
转为大写:
text="hello" result=$(echo "$text" | tr 'a-z' 'A-Z') # 转为大写 echo "$result" # 输出 "HELLO"
-
转为小写:
text="HELLO" result=$(echo "$text" | tr 'A-Z' 'a-z') # 转为小写 echo "$result" # 输出 "hello"
6. 字符串分割
在 Shell 中,字符串分割通常是通过设置 IFS
(内部字段分隔符)来实现的:
text="apple,banana,orange"
IFS=',' read -ra fruits <<< "$text"
# 现在数组 fruits 存储了 ["apple", "banana", "orange"]
for fruit in "${fruits[@]}"; do
echo "$fruit"
done
7. 去除空白
-
去除首尾空白字符(使用
sed
或awk
):text=" Hello, World! " result=$(echo "$text" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//') # 去除首尾空白 echo "$result" # 输出 "Hello, World!"
-
去除中间的空白字符:
text=" Hello World " result=$(echo "$text" | tr -s ' ') # 压缩多个空格为一个 echo "$result" # 输出 "Hello World"
8. 字符串长度
可以使用 ${#string}
来获取字符串的长度:
text="Hello, World!"
length=${#text} # 获取字符串长度
echo "$length" # 输出 13
9. 其他常见字符串操作
-
字符串反转: Shell 本身没有内建的字符串反转函数,但可以用
rev
命令来反转字符串:text="Hello" result=$(echo "$text" | rev) # 使用 rev 命令反转字符串 echo "$result" # 输出 "olleH"
-
检查是否为数字:
text="12345" if [[ "$text" =~ ^[0-9]+$ ]]; then echo "It's a number" else echo "It's not a number" fi
这些是 Shell 脚本中常见的字符串操作方法。在实际使用时,你可以根据需求选择合适的命令和语法。对于更复杂的字符串操作,可以考虑使用外部工具如 awk
、sed
、cut
等。
五、数值的运算
在 Shell 脚本中,数值运算是非常基础且常用的操作之一。无论是在处理数据、计算结果,还是在自动化脚本中进行逻辑判断,数值运算都是不可或缺的一部分。为了满足不同的计算需求,Shell 提供了多种运算方式,如使用常见运算符、$(())
、let
命令和 expr
工具进行运算。
1. 常见运算符
以下是常见的数学运算符及其用途:
运算符 | 说明 | 示例 |
---|---|---|
+ | 加法 | a + b |
- | 减法 | a - b |
* | 乘法 | a * b |
/ | 除法 | a / b |
% | 取余 | a % b |
** | 幂运算 | a ** b |
2.使用 $(( )) 进行算术运算
$(( ))
是最常见的算术运算方式,它支持加法、减法、乘法、除法、取余、幂运算等。例子:
#!/bin/bash
# 加法
a=5
b=3
result=$((a + b))
echo "$a + $b = $result"
# 减法
result=$((a - b))
echo "$a - $b = $result"
# 乘法
result=$((a * b))
echo "$a * $b = $result"
# 除法
result=$((a / b))
echo "$a / $b = $result"
# 取余
result=$((a % b))
echo "$a % $b = $result"
# 幂运算 (注意:bash 4+ 支持)
a=2
result=$((a ** 3))
echo "$a^3 = $result"
3. 使用 let命令进行算术运算
let
命令是另一种进行算术运算的方式,它支持与 $(( ))
类似的操作。使用 let
时,运算结果存储在变量中,不需要额外的赋值符号。
#!/bin/bash
a=5
b=3
# 加法
let result=a+b
echo "$a + $b = $result"
# 乘法
let result=a*b
echo "$a * $b = $result"
4. 使用expr进行算术运算
expr
是一个外部命令,支持基础的算术运算,但要求参数之间要有空格。在脚本中使用时,通常需要加上反引号(``, 用于命令替换)或者
$()`。
#!/bin/bash
a=5
b=3
# 加法
result=$(expr $a + $b)
echo "$a + $b = $result"
# 乘法
result=$(expr $a \* $b)
echo "$a * $b = $result"
注意:在使用 expr
时,运算符如 *
必须转义(加上反斜杠 \
),否则会被 shell 解释为通配符。
5. 浮点数运算
Shell 默认只支持整数运算,如果要进行浮点数运算,可以使用 bc
(Basic Calculator)命令。bc
是一个支持浮点数运算的工具。
#!/bin/bash
a=5
b=3
# 浮点除法
result=$(echo "scale=2; $a / $b" | bc)
echo "$a / $b = $result"
# 浮点加法
result=$(echo "$a + $b" | bc)
echo "$a + $b = $result"
-
scale=2
:设置结果的小数位数,这里是保留两位小数。 -
echo
+| bc
:通过管道将表达式传递给bc
执行计算。
6. 数学运算赋值给变量
-
$(( ))
是最常用且推荐的方式,直接支持赋值给变量。 -
let
可以直接将计算结果赋值给变量,但它不支持括号或复杂表达式。 -
expr
需要将计算结果通过命令替换 ($( )
) 赋值给变量,且运算符如*
必须加上反斜杠进行转义。
6.1. 使用 $(( ))
进行运算并赋值
#!/bin/bash
a=5
b=3
# 将计算结果赋值给变量
result=$((a + b)) # 加法
echo "Result of a + b: $result"
result=$((a - b)) # 减法
echo "Result of a - b: $result"
result=$((a * b)) # 乘法
echo "Result of a * b: $result"
result=$((a / b)) # 除法
echo "Result of a / b: $result"
result=$((a % b)) # 取余
echo "Result of a % b: $result"
6.2. 使用 let
进行运算并赋值
#!/bin/bash
a=5
b=3
# let 会直接修改变量值
let result=a+b # 加法
echo "Result of a + b: $result"
let result=a-b # 减法
echo "Result of a - b: $result"
let result=a*b # 乘法
echo "Result of a * b: $result"
let result=a/b # 除法
echo "Result of a / b: $result"
let result=a%b # 取余
echo "Result of a % b: $result"
6.3. 使用 expr
进行运算并赋值
#!/bin/bash
a=5
b=3
# expr 命令计算并赋值给变量
result=$(expr $a + $b) # 加法
echo "Result of a + b: $result"
result=$(expr $a - $b) # 减法
echo "Result of a - b: $result"
result=$(expr $a \* $b) # 乘法
echo "Result of a * b: $result"
result=$(expr $a / $b) # 除法
echo "Result of a / b: $result"
result=$(expr $a % $b) # 取余
echo "Result of a % b: $result"
7. 使用 let和 $(( ))的区别
-
let
是一种命令,而$(( ))
是一种算术扩展,通常$(( ))
更为灵活,推荐使用。 -
let
只能处理整数运算。 -
$(( ))
更加简洁,且支持较为复杂的数学表达式。
8. 数值运算段落小结
-
$(( ))
是进行算术运算的最常见方法,支持加法、减法、乘法、除法、取余、幂运算等。 -
let
命令也支持算术运算,但更适合简单的场合。 -
expr
命令是一个外部工具,虽然可以进行算术运算,但使用时要注意空格和转义符。 -
对于浮点数运算,
bc
是必不可少的工具。
通过这些方法,你可以在 Shell 脚本中进行各种数学运算,处理整数和浮点数数据。
六、脚本与用户交互
在 Shell 脚本中,与用户的交互是实现动态输入和灵活操作的重要手段。通过命令行参数传递和 read
命令,我们可以让脚本根据不同的输入做出相应的处理。
1.命令行参数传递
1.1.获取的特殊变量
在 Shell 脚本中,参数传递是通过特殊变量来完成的,以下是几个常用的参数变量:
-
$0
:脚本本身的名字。 -
$1, $2, $3, ...
:脚本传递的第一个、第二个、第三个等参数。 -
$#
:传递给脚本的参数个数。 -
$@
:所有传递给脚本的参数,作为一个单独的字符串。 -
$*
:与$@
类似,表示所有参数,但$@
会保留参数的单独性,而$*
将所有参数当作一个字符串来处理。 -
$?
:上一个命令的退出状态(返回值)。 -
$$
:脚本的进程 ID
1.2.示例
echo 执行的文件名是:$0
echo 第一个参数是:$1
echo 第一个参数是:$2
echo 第一个参数是:$3
echo 传递的参数作为一个字符串显示:$*
echo 传递的参数独立作为每个字符串显示:$@
echo 传递到脚本的参数个数是:$#
echo 最后命令的退出状态:$?
echo 脚本运行的当前进程ID是:$$
-
在 Bash 脚本中,参数的传递允许我们根据命令行输入动态地处理数据。脚本通过位置参数(如
$1
,$2
,$3
等)获取用户传递的值,这些值可以是任意文本或数值。如果没有传递参数,脚本会处理为空的值。 -
特殊变量
$*
和$@
则用于获取所有传递的参数,分别作为一个整体字符串或独立的字符串处理。 -
$#
用来获取传递参数的个数,而$?
显示最后执行命令的退出状态,$$
提供当前脚本的进程 ID。 -
这些参数和变量帮助我们在脚本中处理不同的输入,增强脚本的灵活性和功能。
2. read命令交互
在Shell脚本中与用户进行交互时,主要使用的是 read
命令来接收用户输入,read
命令用于从标准输入(通常是键盘)读取一行数据,并将其赋值给指定的变量。它是实现用户输入交互的主要工具。
2.1.基本语法
read [options] variable_name
-
variable_name
:输入的数据将存储到此变量中。 -
options
:一些额外的选项,用来定制输入行为。
2.2.常用参数和选项
-
1.2.1.
-p
:打印提示信息read -p "请输入您的名字: " name echo "您好,$name!"
该选项允许在接收输入之前输出提示信息,提示信息会直接显示在输入框之前。
在上面的例子中,
-p
选项让用户看到“请输入您的名字”提示信息,然后输入名字。用户输入的内容会被存储在name
变量中。
-
1.2.2.
-s
:隐藏输入-s
选项用于隐藏用户输入的内容,常用于密码输入等场合。read -s -p "请输入密码: " password echo "密码已输入,您刚才输入的密码是:$password"
这里,用户输入的内容会被隐藏(不显示在屏幕上),但仍然可以存储在
password
变量中。
-
1.2.3.
-n
:限定输入字符数-n
选项指定用户输入的字符数。当用户输入指定数量的字符后,read
会立即停止输入,不会等待回车。read -n 4 -p "请输入手机号后四位: " input echo "您输入的是: $input"
在上面的例子中,用户只能输入4个字符,输入后会立即显示结果。
-
1.2.4.
-t
:设置超时限制-t
选项允许设置输入超时。如果用户在指定时间内没有输入,read
会自动退出,并返回非零的退出状态。read -t 10 -p "请输入您的名字 (10秒内): " name if [ $? -eq 0 ]; then echo "您好,$name!" else echo "输入超时!" fi
在这个例子中,用户必须在10秒内输入。如果时间超时(没有输入),会显示“输入超时”。
-
1.2.5.
-a
:读取为数组-a
选项允许用户输入多个值,并将这些值保存到一个数组中。用户输入的每个单词(由空格分隔)都会被当作数组元素。read -a colors -p "请输入几种颜色,用空格分隔: " echo "您输入的颜色是: ${colors[@]}"
在这个例子中,用户输入的多个颜色将被存储为数组
colors
,并通过${colors[@]}
打印出来。 -
1.2.6.
-e
:启用编辑模式-e
选项允许启用输入的编辑模式,使得用户可以使用箭头键来编辑输入内容。这通常在交互式脚本中非常有用,但需要在支持编辑模式的环境中使用。read -e -p "请输入一个命令: " command echo "您输入的命令是: $command"
启用
-e
后,用户可以使用键盘上的左右箭头、删除键等编辑输入内容。
2.3.使用多个变量
read
命令可以一次读取多个变量,用户输入的每个空格分隔的值会被依次赋值给指定的变量。
read var1 var2 var3
echo "var1=$var1, var2=$var2, var3=$var3"
如果用户输入了多个值,read
会将每个值分配给相应的变量。如果输入的值少于变量的个数,未赋值的变量会被设置为默认值(通常是空字符串)。
七、if 条件判断
在 Bash 脚本中,if
语句用于根据某个条件是否为真来决定是否执行特定的代码块。
1.基本语法
if [ 条件 ]
then
# 如果条件为真,执行的命令
else
# 如果条件为假,执行的命令(可选)
fi
-
[ 条件 ]
是测试条件的部分。[ ]
是test
命令的简写,表示判断某个条件。 -
then
后面是条件成立时执行的代码块。 -
else
是可选的,表示条件不成立时执行的代码块。 -
fi
是if
语句的结束标志。
PS:[ 条件 ]可以用test来代替但是不推荐用,格式:if test 条件 ;
2.多条件判断语法
2.1if-elif-else
语句
如果有多个条件需要判断,可以使用 elif
来扩展条件判断。
if [ $age -ge 18 ]
then
echo "You are an adult."
elif [ $age -ge 13 ]
then
echo "You are a teenager."
else
echo "You are a child."
fi
输出: 根据 $age
的值,输出对应的年龄段信息。
-
if
用于检查单个条件。 -
if-else
用于条件成立时和不成立时分别执行不同的操作。 -
if-elif-else
用于多个条件判断。
3. 关系运算符
在 Bash 中,关系运算符用于比较数值、字符串等。常用的关系运算符如下:
3.1. 数值比较运算符
运算符 | 说明 | 示例 |
---|---|---|
-eq | 等于 | if [ $a -eq $b ]; then |
-ne | 不等于 | if [ $a -ne $b ]; then |
-gt | 大于 | if [ $a -gt $b ]; then |
-lt | 小于 | if [ $a -lt $b ]; then |
-ge | 大于或等于 | if [ $a -ge $b ]; then |
-le | 小于或等于 | if [ $a -le $b ]; then |
3.1.2. 示例:数值比较
#!/bin/bash
read -p "请输入一个数字:" num
if [ $num -gt 10 ]; then
echo "输入的数字大于 10"
elif [ $num -eq 10 ]; then
echo "输入的数字等于 10"
else
echo "输入的数字小于 10"
fi
3.2. 字符串比较运算符
运算符 | 说明 | 示例 |
---|---|---|
= | 等于 | if [ "$a" = "$b" ]; then |
!= | 不等于 | if [ "$a" != "$b" ]; then |
< | 小于(字典序比较) | if [[ "$a" < "$b" ]]; then |
> | 大于(字典序比较) | if [[ "$a" > "$b" ]]; then |
-z | 字符串为空 | if [ -z "$a" ]; then |
-n | 字符串非空 | if [ -n "$a" ]; then |
3.2.1.示例:字符串比较
#!/bin/bash
read -p "请输入你的名字:" name
if [ "$name" = "Alice" ]; then
echo "你好,Alice!"
else
echo "你好,$name!我找Alice"
fi
4. 逻辑运算符
Bash 中可以使用逻辑运算符来连接多个条件判断。
4.1. 常用逻辑运算符
-
&&
:与运算符(AND),只有当左边和右边的条件都为真时,整个表达式才为真。 -
||
:或运算符(OR),只要左边或右边有一个条件为真,整个表达式为真。 -
!
:非运算符(NOT),将条件反转。
4.1.1. 示例:逻辑与(AND)运算符
#!/bin/bash
echo "请输入两个数字:"
read num1 num2 #使用多个变量输入
if [ $num1 -gt 10 ] && [ $num2 -lt 20 ]; then
echo "第一个数字大于 10 且第二个数字小于 20"
else
echo "条件不成立"
fi
4.1.3. 示例:逻辑或(OR)运算符
#!/bin/bash
echo "请输入两个数字:"
read num1 num2 #使用多个变量输入
if [ $num1 -gt 10 ] || [ $num2 -lt 20 ]; then
echo "至少有一个条件成立"
else
echo "两个条件都不成立"
fi
4.1.4.示例:逻辑非(!)运算符
逻辑非运算符(!
)是布尔值逻辑中非常常见且有用的操作符,用于取反表达式的值。在多种编程语言中,!
的使用方式是类似的,通常用来反转布尔条件,或进行短路逻辑判断。
if ! 条件表达式
then
# 当条件表达式为假时执行的代码
fi
4.1.4.1.判断一个文件是否不存在。
#!/bin/bash
FILE="testfile.txt"
# 使用 ! 运算符判断文件是否不存在
if ! [ -f "$FILE" ]; then
echo "文件 $FILE 不存在"
else
echo "文件 $FILE 已存在"
fi
-
!
运算符用于取反[ -f "$FILE" ]
的结果。如果文件testfile.txt
不存在,那么[ -f "$FILE" ]
为假(返回非零),使用!
后变为真(返回0),进入if
语句的条件分支,输出 "文件 testfile.txt 不存在"。 -
如果文件存在,
[ -f "$FILE" ]
为真(返回0),!
后变为假,程序执行else
部分,输出 "文件 testfile.txt 已存在"。
4.1.4.2.判断命令执行是否失败
#!/bin/bash
# 尝试执行一个命令
if ! ls /nonexistent_directory; then
echo "目录不存在,命令执行失败"
else
echo "目录存在,命令执行成功"
fi
-
! ls /nonexistent_directory
是对ls /nonexistent_directory
命令的取反操作。由于目录/nonexistent_directory
不存在,ls
命令会失败并返回非零值,这时逻辑非运算符!
会将其反转为成功的状态(返回0),从而进入if
语句中的代码块,输出 "目录不存在,命令执行失败"。
5. if 语句的结合 read和参数传递
可以通过将 read
命令与传递的脚本参数相结合,在脚本中创建交互式的输入或根据参数进行判断。
5.1. 示例:if判断结合read完成登录校验功能
假设我们有一个脚本,它通过 read
获取的输入做出不同的响应。
-
首先提示用户输入用户名。
-
如果用户名为空,则提示用户输入不能为空。
-
如果用户名不为空,则继续提示用户输入密码。
-
最后通过
if
判断用户名和密码是否符合预定条件(用户名为 "admin" 和密码为 "1234")。
#!/bin/bash
# 提示用户输入用户名
read -p "请输入用户名:" username
# 判断用户名是否为空
if [ -z "$username" ]; then
echo "用户名不能为空!"
else
# 提示用户输入密码
read -s -p "请输入密码:" password
# 假设用户名为 "admin" 和密码为 "1234" 才为有效
if [ "$username" == "admin" ] && [ "$password" == "1234" ]; then
echo "登录成功!"
else
echo "用户名或密码错误。"
fi
fi
5.2. 示例:根据参数判断并结合 read
假设我们有一个脚本,它根据用户输入的参数或通过 read
获取的输入做出不同的响应。
-
使用
if [ $# -gt 0 ]
判断脚本是否接收到命令行参数。如果接收到参数,则使用input=\$1
来将第一个参数赋值给input
。 -
如果没有传入参数,脚本会使用
read
命令要求用户输入一个数字。 -
最后,使用
if
判断输入的数字是否大于 10,并输出相应的信息。
#!/bin/bash
# 判断是否有传入参数
if [ $# -gt 0 ]; then
# 如果有传入参数,则使用第一个参数
input=\$1
else
# 如果没有传入参数,使用 `read` 命令从用户获取输入
read -p "请输入一个数字:" input
fi
# 判断输入的数字
if [ $input -gt 10 ]; then
echo "输入的数字大于 10"
else
echo "输入的数字小于或等于 10"
fi
6. if条件判断段落小结
-
if
语句:用于根据条件执行不同的命令。可以结合数值、字符串比较,以及逻辑运算符(AND、OR、NOT)来构建复杂的条件判断。 -
关系运算符:用于数值和字符串的比较,常见的包括
-eq
,-ne
,-gt
,-lt
,-ge
,-le
等。 -
逻辑运算符:用于连接多个条件判断,如
&&
(与)、||
(或)和!
(非)。 -
read
与参数传递结合:通过脚本参数和read
的结合,可以实现更加灵活的输入方式,既可以通过命令行传递参数,也可以通过read
进行交互式输入。
这些基本的条件判断和逻辑运算符是 Bash 脚本中非常重要的组成部分,掌握它们能让你编写更加智能和灵活的脚本。
八、循环
1. for 循环
for
循环是一种用于遍历列表(可以是数字范围、文件列表或命令输出等)的控制结构。它重复执行命令直到列表中的所有元素都被处理完。
1.1.for循环语法
for var in list
do
# 执行的命令
done
-
var
是循环变量,依次从list
中获取值。 -
list
可以是一个数字范围、字符串列表或命令输出等。
1.2.示例:遍历数字列表
for i in 1 2 3 4 5
do
echo "Number: $i"
done
在这个例子中,for
循环会依次将数字 1 到 5 赋给变量 i
,然后执行 echo
命令。
1.3.示例:使用命令输出作为列表
for file in $(ls /etc)
do
echo "File: $file"
done
输出: 列出 /etc
目录中的所有文件或子目录。
-
已知需要遍历的元素(如数字、文件、命令输出等)。
-
当循环次数是预先确定的。
2. while 循环
while
循环会根据指定的条件判断,在条件为真时不断重复执行指定的命令。它适用于当你不确定循环的具体次数,但需要根据某个条件来决定是否继续循环。
2.1. while 循环语法
while condition
do
# 执行的命令
done
-
condition
:循环的条件,只要条件返回true
,循环就会继续执行。 -
commands
:条件为真时要执行的命令。
2.2.示例:计数循环
i=1
while [ $i < 5 ]
do
echo "Number: $i"
i=$((i + 1)) # 递增 i
done
在这个例子中,while
循环会检查 i
是否小于或等于 5,只要条件成立,就执行 echo
命令并增加 i
的值。如果为假则中断循环。
2.3.示例:直到某个条件满足为止
while ! curl -s http://example.com > /dev/null
do
echo "Waiting for website to be up..."
sleep 5
done
这个例子在网站不可达时会每 5 秒重试一次,直到网站可用为止。
3. until 循环
until
循环的语法类似于 while
循环,但它的条件是 当条件为假时,循环会继续执行。可以理解为 while
循环的条件是 为真时,而 until
则相反,条件为假时执行。
语法:
until [ 条件 ]
do
# 循环体
done
例子:
例子:
#!/bin/bash
count=1
until [ $count -gt 10 ] # 直到 count 大于 5 时退出
do
echo "Count is: $count"
((count++)) # 增加 count
done
3.1.代码功能概述
-
until
循环:只有在循环条件为 真 时才退出。这里的条件是count -gt 10
,当count
达到 11 时,条件成立,循环停止。 -
通过
count++
,每次循环结束后,count
会递增 1,直到达到退出条件。
3.2.循环条件分析
-
until [ $count -gt 10 ]
:-
until
循环会一直执行,直到指定的条件为 真。 -
在这个例子中,条件是
[ $count -gt 10 ]
,表示当count
大于 10 时,条件为真。 -
只有当
count
大于 10 时,until
循环才会停止执行。
-
3.3.执行过程
-
初始时,
count=1
。 -
循环开始时,检查条件
[ $count -gt 10 ]
。由于count
初始为 1,条件1 -gt 10
为假,因此继续执行循环体。 -
输出当前
count
的值:Count is: 1
。 -
然后
count
增加 1,count
变为 2。 -
再次检查条件
[ $count -gt 10 ]
,此时count
为 2,条件2 -gt 10
仍为假,继续执行循环。 -
依此类推,直到
count
增加到 11。 -
当
count
为11 时,检查条件[ $count -gt 10 ]
,此时条件为真,循环退出。
4.case 语句
case
语句用于多重条件匹配,它根据变量的值与多个模式进行比较,执行匹配成功的块,类似于其他语言中的 switch
语句。
语法:
case "$变量" in
模式1)# 当变量匹配模式1时执行
;;
模式2)# 当变量匹配模式2时执行
;;
*)# 默认情况
;;
esac
例子:
#!/bin/bash
read -p "请输入一个数字:" num
case $num in
1)echo "你输入的是数字 1"
;;
2)echo "你输入的是数字 2"
;;
3)echo "你输入的是数字 3"
;;
*)echo "输入的数字不在预期范围内"
;;
esac
4.1.代码功能概述
该脚本使用 case
语句根据用户输入的数字输出不同的提示信息。用户输入的数字将与预设的 1
、2
、3
进行匹配。如果输入的数字是其中之一,则输出相应的消息;如果输入的数字不在预期的范围内(即不等于 1
、2
、3
),则输出默认的提示消息。
4.2.循环条件分析
虽然这段脚本中没有使用显式的 循环(例如 for
或 while
循环),但它依然有条件判断逻辑。具体来说,case
语句会根据用户输入的数字值执行不同的分支,类似于条件分支的“匹配”过程。
具体流程:
-
用户通过
read -p
提示输入一个数字,将该数字存储在变量num
中。 -
case语句根据num的值执行以下匹配:
-
如果用户输入的是
1
,输出"你输入的是数字 1"
。 -
如果用户输入的是
2
,输出"你输入的是数字 2"
。 -
如果用户输入的是
3
,输出"你输入的是数字 3"
。 -
如果用户输入的是其他数字或字符(与预设的
1
、2
、3
都不匹配),则会执行默认分支,输出"输入的数字不在预期范围内"
。
-
5.总结对比
结构 | 语法特点 | 适用场景 |
---|---|---|
for 循环 | 遍历列表中的元素 | 已知元素列表或循环次数 |
while 循环 | 条件为真时继续执行 | 条件判断后执行,直到条件不再满足 |
until 循环 | 条件为假时继续执行 | 直到某条件满足才停止 |
case 语句 | 基于匹配的值进行条件判断 | 替代多个 if-else 分支,类似于其他语言中的 switch 语句。 |
九、函数
在Shell脚本中,函数(function)是用来组织和封装代码块的一种方式,它能够帮助你提高代码的可读性和可重用性。我们可以将多次需要执行的命令、语句块或逻辑封装成一个函数,调用时只需提供函数名,Shell会自动执行对应的代码。
-
Shell函数的参数可以通过
$1
,$2
,$@
,$#
等方式访问。 -
使用
local
关键字定义局部变量,避免变量污染。 -
函数的返回值通常通过
echo
输出,而return
语句通常用于返回状态码。 -
使用
shift
可以处理函数中的多参数,递归也可以在Shell函数中实现。
1. 函数的定义与使用
在Shell脚本中,函数是通过 function
关键字或直接使用函数名来定义的。可以通过 ()
定义函数,也可以通过 return
语句返回函数的结果。
语法:
# 定义函数
function my_function {
# 函数体
}
# 或者可以直接使用下面的方式
my_function() {
# 函数体
}
两者是等价的,选择哪种方式主要取决于个人或团队的代码风格。传统方式比较简洁,而带 function
关键字的方式可能显得更直观,特别是当函数非常复杂时。
例子:
#!/bin/bash
# 定义一个简单的函数
greet() {
echo "Hello, $1!" # $1 是传入的第一个参数
}
# 调用函数
greet "tom"
greet "你见过龙吗?"
2. 函数的传参
Shell函数可以接收外部传递的参数,函数的参数是通过 $1
、$2
、$3
、$0
、$@
、$#
等位置变量来访问的,这些变量代表了传递给函数的参数。
-
$1
,$2
,$3
:分别代表了传入参数的第一个、第二个、第三个位置。 -
$0
:表示脚本本身的名称。 -
$@
:表示传递给函数的所有参数,作为一个单独的字符串数组来处理。 -
$#
:表示传递给函数的参数个数。
例子:
#!/bin/bash
# 定义一个函数,计算两个数的和
sum() {
local num1=$1 # 第一个参数
local num2=$2 # 第二个参数
echo "Sum is: $((num1 + num2))"
}
# 调用函数,传递两个参数
sum 5 7
sum 10 20
3. 局部变量(local)
在Shell脚本中,默认情况下,所有变量的作用范围是全局的,这意味着如果在一个函数内部修改了一个变量的值,它将影响到函数外部的相同变量。如果我们想在函数内部定义一个仅在该函数范围内有效的变量,可以使用 local
关键字。
#!/bin/bash
my_function() {
local var="local_variable"
echo "Inside function: $var"
}
# 在函数外部访问同名变量
var="global_variable"
my_function
echo "Outside function: $var"
可以看到,local
关键字确保了 var
变量仅在 my_function
内部有效,函数外部的 var
变量没有受到影响。
3.1.为什么使用 local
?
-
避免命名冲突:当一个脚本中有多个函数时,如果多个函数使用相同的变量名而没有使用
local
,它们可能会相互覆盖,造成意外的结果。使用local
可以有效避免这种情况。 -
封装性:使用
local
可以将变量的作用域限制在函数内部,提升代码的可维护性和可读性。
4. 函数返回值
Shell函数默认没有返回值,但你可以使用 return
语句返回一个整数值。需要注意的是,return
语句只能返回 0 到 255 之间的整数(通常用于表示状态码)。
例子:
#!/bin/bash
# 定义一个函数,检查文件是否存在
check_file() {
if [ -f "$1" ]; then
return 0 # 文件存在
else
return 1 # 文件不存在
fi
}
# 调用函数并检查返回值
check_file "/etc/passwd"
if [ $? -eq 0 ]; then
echo "文件存在"
else
echo "文件不存在"
fi
5. 函数中的递归调用
Shell支持函数的递归调用,也就是函数在自身内部调用自身。递归调用通常需要一个基准条件来防止无限递归。
递归示例:计算阶乘
#!/bin/bash
factorial() {
if [ $1 -le 1 ]; then
echo 1
else
local result=$(( $1 * $(factorial $(( $1 - 1 )) ) ))
echo $result
fi
}
result=$(factorial 5)
echo "Factorial of 5 is: $result"
6. 内建函数与外部函数的区别
-
内建函数:Shell本身内置的命令和函数(例如
echo
,read
,test
,pwd
等),这些函数不需要额外定义,Shell会直接识别。 -
外部函数:通过 Shell 脚本或程序中定义的函数。这些函数是用户自定义的,不是Shell内建的。
十、不同脚本的互相调用
Shell 脚本可以通过几种方式调用其他脚本或命令
1.直接调用
我们可以直接在一个脚本中调用另一个脚本,方法是提供脚本路径或脚本名称。如果另一个脚本在当前目录下并且有执行权限,可以直接调用。
#initiate.sh
echo 'Hello,world'
#other_script.sh
# 调用其他脚本
./initiate.sh
但是这种方式是需要initiate.sh拥有被执行权限的。
2.使用 source 或 . 命令
source
命令或者 .
(点命令)可以在当前 Shell 环境中执行另一个脚本,而不会启动一个新的子进程。这意味着如果被调用的脚本修改了环境变量或其他设置,这些修改会影响当前脚本。
#initiate.sh
name=Tom
age=18
#other_script.sh
# 使用 source 或 . 来执行脚本
source initiate.sh
echo "Hello, my name is $name and I am $age years old"
# 或者
. initiate.sh
echo "Hello, my name is $name and I am $age years old"
这种方式通常用于共享函数、变量或环境设置等。
3.使用 bash 命令调用
我们也可以通过 bash
命令调用另一个脚本,并启动一个新的子 Shell 来执行脚本。
#initiate.sh
echo 'Hello,world'
#other_script.sh
bash initiate.sh # 调用其他脚本
这种方式和直接调用的区别是,不需要执行权限,直接通过bash解释器来调用sh脚本。
4.传递参数给脚本
在调用其他脚本时,可以传递参数,接收脚本中的 $1
, $2
, … 等位置参数。
#initiate.sh
echo "Hello, my name is $1 and I am $2 years old"
#other_script.sh
bash initiate.sh ikun 2.5
在 initiate.sh
中,可以通过 $1
, $2
等获取传递的参数。
十一、重定向操作
1.输出重定向(>)
将命令的标准输出重定向到指定的文件中。如果文件存在,重定向会覆盖文件内容;如果文件不存在,则会创建该文件。
echo "Hello, World!" > output.txt
cat output.txt
ls > output.txt
cat output.txt
2.追加输出(>>)
将命令的标准输出追加到指定文件的末尾,而不会覆盖文件原有内容。
echo "Hello" > output.txt
echo "My name is ikun and I am 2.5 years old" >> output.txt
cat output.txt
3.输入重定向(<)
将文件的内容作为命令的输入。例如,可以通过重定向从文件中读取输入,而不需要通过标准输入。
sort < output.txt
cat < output.txt
4.错误输出重定向(2>)
将标准错误(stderr)输出重定向到文件中。标准错误通常用于显示错误信息。
ikun 2> error.log
cat error.log
5.同时重定向标准输出和标准错误
有时我们需要将标准输出和标准错误都重定向到同一个文件。可以通过以下方式:
ikun > output.txt 2>&1
ls >> output.txt 2>&1
cat output.txt
这将同时将标准输出和标准错误输出重定向到 output.txt
文件中。
6.文件描述符介绍
在 Shell 脚本中,文件描述符 是对输入输出流的抽象。Shell 默认有三个文件描述符:
-
标准输入(stdin):文件描述符
0
,通常用于接收来自键盘或文件的输入。 -
标准输出(stdout):文件描述符
1
,通常用于输出到终端或文件。 -
标准错误(stderr):文件描述符
2
,通常用于输出错误信息。
文件描述符使得我们能够灵活控制输入输出流。可以通过以下方式进行更复杂的输入输出操作:
6.1.重定向文件描述符
通过重定向文件描述符,可以将标准输入、输出或错误输出指向其他文件或设备。
-
将标准输出重定向到文件:
echo "This is an info message" > output.txt
-
将标准错误输出重定向到文件:
ikun 2> error.log
-
同时将标准输出和标准错误输出重定向到同一个文件:
ikun > output.log 2>&1
6.2.使用 exec
进行更复杂的重定向
exec
命令可以在脚本中为特定文件描述符分配新的文件。
例如,以下命令会将文件描述符 3
重定向到文件 myfile.log
:
exec 3> myfile.log
echo "This goes to myfile.log" >&3
关闭文件描述符:如果不再需要某个文件描述符,可以使用 exec
来关闭它。
exec 3>&-
结语
通过本系列的学习,我们深入探讨了 Shell脚本 的各个核心概念和常用技巧。从基本的 变量定义、条件判断、循环结构,到更复杂的 脚本参数传递、数学运算 和 脚本间调用,每一部分都为我们编写高效、简洁的脚本打下了坚实的基础。
在这段学习旅程中,我们掌握了如何使用 if 条件判断 来控制程序的执行流程,如何通过 for、while 和 until 等循环结构来处理重复任务,还了解了 case 语句 和 函数 的灵活应用。此外,掌握了 字符串操作 和 环境变量管理,使得我们能够更好地与系统进行交互,并编写出更具有可维护性和可扩展性的脚本。
Shell脚本不仅是系统管理员、DevOps 工程师和开发人员日常工作的重要工具,它还帮助我们简化了复杂的任务,自动化了很多重复性操作,提高了工作效率。
脚本语言的魅力在于其简洁与强大,而 Shell 脚本更是 Unix/Linux 系统中的重要组成部分。无论是处理文件操作、自动化备份、配置系统,还是进行日志分析和任务调度,Shell 脚本都能够为我们的工作带来巨大的便利。
通过本系列的学习,相信大家已经具备了编写、调试和优化 Shell 脚本的能力。希望你们能够将这些知识应用到实际的工作中,不断提高脚本的质量与效率,探索更多自动化和高效工作的可能性。
最后,Shell脚本学习之路没有尽头,技术的深度和广度总是值得我们不断探索和进步。祝愿大家在今后的脚本编写过程中,能够不断突破自己,遇见更多挑战与机遇。
往期文章
shell脚本_创建执行与变量的定义与使用-CSDN博客
shell脚本_永久环境变量和字符串操作和shell如何变成永久变量-CSDN博客
shell脚本_脚本参数传递与数学运算-CSDN博客
shell脚本_脚本与用户交互以及if条件判断-CSDN博客
shell脚本_if条件判断与for循环结构讲解与示例-CSDN博客
shell脚本_for循环与while循环讲解和对比-CSDN博客
shell脚本_until循环和case语句与函数的定义和使用-CSDN博客
shell脚本_不同脚本的互相调用和重定向操作-CSDN博客