shell脚本的基础
一、shell基础语法
1、什么是shell
Shell(Shell 解释器)是一种命令行界面(CLI)程序,它允许用户与计算机操作系统进行交互。通过 shell,用户可以输入命令并接收计算机系统的响应。Shell 还可以执行各种系统管理任务、运行程序、处理文件等操作。
有许多不同类型的 shell,每种都有其自己的特性和功能。以下是一些常见的 shell:
-
Bourne Shell (sh): 是 Unix 系统上最早的 shell,它具有基本的命令解释功能。许多 Unix 和 Linux 系统都包含这个 shell 的变体。
-
Bash (Bourne Again Shell): 是一个流行的 Unix/Linux shell,是 sh 的增强版,具有更多功能和特性,如命令历史、自动补全等。大多数 Linux 发行版默认使用 Bash 作为默认 shell。
-
C Shell (csh): 提供了一些与 C 语言相似的语法,适合编程和交互式使用。
-
Korn Shell (ksh): 是一种 Unix shell,结合了 sh 和 csh 的特性,提供了更丰富的编程和命令解释功能。
-
Zsh (Z Shell): 是一个功能强大的 shell,具有自动补全、插件支持等高级功能。
-
PowerShell: 是 Windows 系统上的强大命令行和脚本语言环境,它使用对象和.NET 框架来进行管理任务。
Shell 是与操作系统交互的一种方式,它允许用户通过键入文本命令来控制计算机。用户可以执行文件操作、运行程序、管理文件系统、进行网络操作、查看和修改系统设置等等。Shell 还可以通过脚本编程来自动化一系列任务,这对系统管理员和开发人员特别有用。
总之,Shell 是一种重要的系统工具,使用户能够以命令行方式与操作系统进行交互和操作。不同的 shell 提供了不同级别的功能和灵活性,可以根据需要选择适合的 shell。
2、什么是shell脚本
Shell 脚本是一系列 shell 命令和控制结构的文本文件,用于执行一系列特定的任务和操作。这些脚本允许用户将一组命令按照特定的顺序和逻辑组织起来,从而实现自动化任务、系统管理、数据处理等操作。
Shell 脚本通常由一个文本编辑器创建,其中包含一系列要执行的命令。这些命令可以是任何您在命令行中输入的标准 shell 命令,如文件操作、进程管理、网络操作等。
以下是一些示例说明 shell 脚本的特点和用途:
-
自动化任务: Shell 脚本可以用于自动化一系列操作。例如,您可以创建一个备份脚本,以定期备份重要文件并将其存储在指定的位置。
-
系统管理: 系统管理员可以使用 shell 脚本来执行常见的系统管理任务,如系统监控、日志清理、用户管理等。
-
数据处理: Shell 脚本对于处理文本文件和数据非常有用。您可以编写脚本来解析日志文件、提取信息、格式化输出等。
-
程序启动和配置: 在程序运行之前,可以使用脚本来设置环境变量、检查依赖项并启动所需的服务。
-
批量处理: 如果您需要在多个文件或目录上执行相同的操作,可以编写脚本来批量处理它们。
一个简单的 shell 脚本示例:
#!/bin/bash
# 这是一个简单的 shell 脚本示例
echo "Hello, world!" # 打印消息到屏幕
ls -l # 列出当前目录的文件
在上面的示例中,脚本使用 echo
命令打印一条消息,然后使用 ls -l
命令列出当前目录中的文件。
要运行一个 shell 脚本,您需要确保脚本文件具有可执行权限,并在命令行中输入脚本的文件名。例如,如果脚本文件名为 myscript.sh
,您可以使用以下命令运行它:
chmod +x myscript.sh # 添加可执行权限
./myscript.sh # 运行脚本
总之,shell 脚本是一种强大的工具,可以帮助自动化重复性任务,管理系统,并在命令行环境中执行一系列操作。不同的 shell(如 Bash、Zsh 等)都支持脚本编程。
3、shell编程语言
在 Bash Shell 脚本中,第一行通常被称为 "shebang" 或 "hashbang" 行,用于指定要用于执行脚本的解释器。这一行告诉系统要使用哪个程序来解释执行脚本中的命令。在 Bash Shell 脚本中,常见的第一行是:
#!/bin/bash
这里的 #!/bin/bash
意味着使用 Bash 解释器来执行脚本中的命令。#!
是 "shebang" 的标识符,后面跟着指定解释器的路径。在这种情况下,/bin/bash
是 Bash 解释器的路径。
这一行告诉系统,在执行脚本时,请使用指定的解释器来解释脚本中的命令。这使得您可以在脚本的开头指定任何支持的解释器,例如 Python、Perl 等。
在创建 Bash 脚本时,确保第一行是正确的 "shebang" 行,以确保您的脚本能够正确地执行。此外,确保指定的解释器路径在系统中是正确的,以便系统能够找到并使用相应的解释器来运行脚本。
Shell 脚本可以通过几种不同的方式来执行,具体取决于您的需求和环境。以下是几种常见的执行方法:
-
直接执行:在命令行中直接调用脚本文件,前提是脚本文件具有可执行权限。在终端中输入以下命令:
./script.sh
其中
script.sh
是您的脚本文件名。 -
使用 Bash 执行:如果您的脚本需要 Bash 解释器来执行,您可以在终端中使用以下命令:
bash script.sh
或者使用绝对路径:
/bin/bash script.sh
-
使用其他 Shell 解释器执行:根据您的脚本语言和需求,您可以使用不同的 Shell 解释器来执行脚本。例如,如果您的脚本需要使用 Zsh 解释器执行,您可以使用以下命令:
zsh script.sh
-
添加可执行权限并将脚本路径添加到 PATH:您可以将脚本所在的目录添加到系统的 PATH 环境变量中,这样您就可以在任何地方直接执行脚本。首先,确保脚本文件具有可执行权限,然后将脚本所在目录添加到 PATH 中。
-
使用绝对路径执行:如果您知道脚本的绝对路径,您可以在终端中直接使用绝对路径来执行脚本。
-
通过管道或重定向执行:您可以将脚本与其他命令一起使用,通过管道或重定向将其他命令的输出作为脚本的输入。
sh < script.sh
-
使用定时任务执行:您可以使用定时任务工具(如 cron)来定期执行脚本。这在自动化和定期任务方面非常有用。
请注意,为了能够执行脚本,脚本文件必须具有可执行权限。您可以使用 chmod
命令为脚本添加执行权限:
chmod +x script.sh
选择适合您情况的执行方法,以确保脚本能够成功执行。
4、shell中的符号
符号 | 说明 |
---|---|
; | 分隔多个命令。 |
| | 将一个命令的输出作为另一个命令的输入。 |
>、>> | 重定向命令的输出到文件,> 覆盖文件内容,>> 追加到文件末尾。 |
< | 将文件内容作为命令的输入。 |
2> | 将命令的错误输出重定向到文件。 |
& | 使命令在后台运行,允许您继续输入其他命令。 |
" " | 创建包含变量和转义字符的字符串,变量会被展开。 |
' ' | 创建不展开变量和转义字符的字符串。 |
` 或 $() | 在命令中嵌套执行其他命令。 |
*、?、[] | 用于匹配文件名的通配符。 |
\ | 用于转义特殊字符,使其成为字面字符。 |
# | 用于添加注释。整行或行尾的内容将被视为注释。 |
二、shell的变量与应用
1、shell变量
在 Shell 编程中,变量是用于存储数据值或字符串的名称。变量使您能够在脚本中临时存储和操作数据,以便后续使用。以下是关于 Shell 中变量的一些重要信息:
-
变量的命名规则:
-
变量名只能由字母、数字和下划线组成。
-
变量名不能以数字开头。
-
变量名区分大小写,例如
myVar
和myvar
是不同的变量名。
-
-
定义变量: 在 Bash 中,可以使用等号
=
来定义变量并给其赋值,如variable_name=value
。等号两边不能有空格。例如:name="John" age=25
-
访问变量的值: 使用
$
符号加上变量名来获取变量的值。例如:echo $name
。 -
单引号和双引号:
-
在双引号中,变量会被展开。例如:
echo "My name is $name"
-
在单引号中,变量不会被展开。例如:
echo 'My name is $name'
-
-
特殊变量:
-
$0
:当前脚本的名称。 -
$1
、$2
、...:脚本的参数,例如$1
是第一个参数。 -
$#
:传递给脚本的参数数量。 -
$*
或$@
:所有参数的列表。 -
$?
:上一个命令的退出状态。0表示没有错误,其他任何值表明有错误 -
$$
:当前脚本的进程 ID。
-
-
删除变量: 使用
unset
命令来删除变量。例如:unset variable_name
-
特殊字符和空格: 如果变量的值包含空格或特殊字符,应将其放在双引号中,以防止解释错误。例如:
text="Hello, world!"
-
环境变量: 环境变量是一种全局变量,用于存储系统的配置和信息。您可以使用
export
命令将变量提升为环境变量。例如:export MY_VARIABLE=value
-
变量赋值: 变量可以通过赋值运算符重新赋值。例如:
age=30
将age
变量的值更改为 30。
Shell 中的变量是进行编程和脚本编写的关键元素,它们使您能够存储、操作和处理数据。了解如何使用和管理变量是 Shell 编程的基础之一。
在 Shell 编程中,变量可以根据其作用域和性质进行分类。主要的变量分类包括:
-
局部变量(Local Variables: 局部变量在函数内部定义,只在函数内部可见。它们不会影响到其他函数或脚本的变量。局部变量的作用域仅限于定义它们的函数。
-
环境变量(Environment Variables): 环境变量在整个系统中可用,它们包含有关系统环境和配置的信息。您可以使用
export
命令将局部变量提升为环境变量,使其在当前 shell 会话中可见。 -
位置参数变量(Positional Parameters): 位置参数变量用于在脚本中传递参数。例如,
$1
表示第一个参数,$2
表示第二个参数,以此类推。位置参数变量的作用域是整个脚本。 -
特殊变量(Special Variables): 特殊变量由 Shell 预先定义,用于提供有关脚本和环境的信息。例如,
$0
表示脚本的名称,$?
表示上一个命令的退出状态。 -
全局变量(Global Variables: 全局变量在整个脚本中可见,但它们不同于环境变量。它们在脚本内部定义,但不会成为环境变量。
在 Bash Shell 中,通常变量默认是局部的。如果您希望变量在脚本中的所有函数中都可见,您可以使用 declare
命令来声明全局变量。
下面是一些示例变量分类的示例:
-
局部变量:
function my_function() { local var1="local_var" echo $var1 } my_function # 输出: local_var echo $var1 # 输出: (空)
-
环境变量:
export MY_VARIABLE="env_var"
-
位置参数变量:
echo "First argument: $1" echo "Second argument: $2"
-
特殊变量:
echo "Script name: $0" echo "Exit status of previous command: $?"
-
全局变量:
declare -g global_var="global_variable"
不同类型的变量在不同的作用域中起作用,了解它们的作用和使用方式可以帮助您更有效地编写 Shell 脚本。
2、shell变量的永久生效
要使变量永久生效,通常需要将变量添加到适当的配置文件中,以便在系统启动时自动加载。这可以通过以下几种方法来实现,具体取决于你希望变量在哪些环境中可用:
-
全局环境变量(对所有用户生效):
将变量添加到
/etc/environment
文件中。编辑该文件并在末尾添加类似以下的行:MY_VARIABLE=my_value
保存文件后,新的环境变量将在系统启动时生效。
-
用户级别的永久环境变量:
对于特定用户,你可以编辑该用户的
~/.bashrc
或~/.bash_profile
文件,根据需要添加变量。这将确保在用户登录时加载这些变量。echo 'export MY_VARIABLE=my_value' >> ~/.bashrc source ~/.bashrc # 立即使变量生效,或者重新登录
或者,如果你希望变量对所有 shell 都可用(不仅仅是 Bash),你可以将变量添加到用户的
~/.profile
文件中。 -
特定服务或程序的环境变量:
对于特定的服务或程序,你可以编辑其相关的启动脚本或配置文件,并在其中设置环境变量。例如,对于系统服务,通常可以在
/etc/systemd/system/
目录中的服务单元文件中添加环境变量。Environment="MY_VARIABLE=my_value"
修改完毕后,使用以下命令重载 systemd 配置并重新启动服务:
systemctl daemon-reload sudo systemctl restart your-service-name
确保在进行任何更改之前备份配置文件,以防止意外损坏。最后,为了使更改生效,你可能需要重新启动系统、重新登录用户或重启相应的服务。
3、命令替换语法
在 Bash Shell 中,$( )
是一种用于命令替换的语法。它允许您在命令中嵌套执行其他命令,并将嵌套命令的输出作为外部命令的一部分。这样可以将一个命令的输出直接嵌入到另一个命令中。
例如,如果您想获取当前工作目录并将其传递给另一个命令,您可以使用 $( )
进行命令替换:
echo "Current directory: $(pwd)"
在这个例子中,$(pwd)
会被当前工作目录的路径替换,然后整个表达式将传递给 echo
命令。
另一个例子是获取某个目录下文件的数量:
files_count=$(ls -l | wc -l)
echo "Number of files in the directory: $files_count"
在这个例子中,$(ls -l | wc -l) 会先执行 ls -l 命令来列出文件,然后通过管道传递给 wc -l 命令以计算行数,最终得到目录中文件的数量。
[root@fishman-160 usr]# VAR2=mysql
[root@fishman-160 usr]# echo ${VAR2}-$(date +"%F").LOG
mysql-2023-08-23.LOG
例:把打包结果复制给变量
[root@fishman-160 ~]# VAR3=$(tar cvf b.tar $(find /root -maxdepth 1 -name "[a-f].txt" ))
tar: 从成员名中删除开头的“/”
tar: 从硬连接目标中删除开头的“/”
[root@fishman-160 ~]# tar -tf b.tar
root/a.txt
root/d.txt
root/f.txt
root/b.txt
root/e.txt
root/c.txt
[root@fishman-160 ~]# echo $VAR3
/root/a.txt /root/d.txt /root/f.txt /root/b.txt /root/e.txt /root/c.txt
[root@fishman-160 ~]# mkdir days1 && cd days1 && touch {a..f}.txt
[root@fishman-160 days1]# ls
a.txt b.txt c.txt d.txt e.txt f.txt
[root@fishman-160 days1]# VAR5=$(tar cvf b.tar $(find /root/days1/ -name "*.txt"))
tar: 从成员名中删除开头的“/”
tar: 从硬连接目标中删除开头的“/”
[root@fishman-160 days1]# tar -tf b.tar
root/days1/a.txt
root/days1/b.txt
root/days1/c.txt
root/days1/d.txt
root/days1/e.txt
root/days1/f.txt
[root@fishman-160 days1]# echo $VAR5
/root/days1/a.txt /root/days1/b.txt /root/days1/c.txt /root/days1/d.txt /root/days1/e.txt /root/days1/f.txt
[root@fishman-160 days1]# VAR6=$(tar cf b.tar $(find /root/days1/ -name "*.txt"))
tar: 从成员名中删除开头的“/”
tar: 从硬连接目标中删除开头的“/”
[root@fishman-160 days1]# echo $VAR6
4、数学运算
在shell中,双括号 ((...))
是一种用于数学计算的结构,用于执行数学表达式并将结果赋给变量。在这种情况下,result=$((5 + 3))
使用了双括号来表示内部的数学表达式,计算表达式 5 + 3
的结果,并将结果赋给变量 result
。
运算符 | 描述 | 示例 |
---|---|---|
+ |
执行加法操作 | result=$((5 + 3)) |
- |
执行减法操作 | result=$((10 - 2)) |
* |
执行乘法操作 | result=$((4 * 6)) |
/ |
执行除法操作(整数除法,截断小数部分) | result=$((15 / 2)) |
% |
获取两数相除的余数 | result=$((17 % 3)) |
** |
计算一个数的指数幂 | result=$((2 ** 4)) |
= |
将值赋给变量 | result=10 |
++ |
将变量的值增加1 | num++ |
-- |
将变量的值减少1 | num-- |
双括号 ((...))
在shell中有以下特点:
-
数学计算环境: 双括号创建一个数学计算环境,允许您在其中执行数学运算,包括整数运算、逻辑运算等。
-
支持表达式: 您可以在双括号中使用常见的数学运算符(如
+
、-
、*
、/
、%
、**
),以及逻辑运算符(如&&
、||
)等。 -
变量操作: 双括号中可以直接使用变量,而不需要使用
$
符号进行变量展开。例如,((result = num1 + num2))
可以直接将计算结果赋给result
变量。 -
支持自增和自减: 双括号中可以使用自增
++
和自减--
运算符来操作变量的值。 -
逻辑判断: 双括号中可以执行逻辑判断,并使用逻辑运算符来组合条件。
-
高级数学运算: 双括号允许执行较为复杂的数学运算,包括位运算、比较操作等。
-
不需要
$((...))
: 在双括号内,您无需使用外部的$((...))
来引用数学表达式,直接使用表达式即可。
综合来说,双括号提供了一个更灵活、更强大的数学计算环境,方便进行数值计算和逻辑操作,而且不需要过多的符号,使得表达式更加清晰。但需要注意,双括号只在支持这种语法的shell中有效,如Bash。
5、expr命令
expr
是一个在Unix/Linux shell中用于执行基本数学和字符串操作的命令。它可以用于进行简单的数学运算、字符串比较和模式匹配。以下是一些 expr
命令的示例用法:
-
数学运算:
result=$(expr 5 + 3) echo $result # 输出: 8
-
字符串比较:
string1="hello" string2="world" if [ $(expr "$string1" = "$string2") -eq 1 ]; then #$(expr "$string1" = "$string2") 的执行结果将会是 0。这是因为 expr 命令在字符串比较时,如果字符串相等则返回 1,如果不相等则返回 0。 echo "Strings are equal." else echo "Strings are not equal." fi
-
长度计算:
string="abcdef" length=$(expr length "$string") echo "Length: $length" # 输出: 6
-
子字符串提取:
string="abcdefgh" substring=$(expr substr "$string" 3 4) echo "Substring: $substring" # 输出: cdef
-
模式匹配:
string="example123" if [ $(expr "$string" : '^[a-z]*[0-9]*$') -gt 0 ]; then echo "Pattern matched." else echo "Pattern not matched." fi
需要注意的是,expr
命令在执行数学运算时,使用的是基本的整数运算,并且运算符之间需要有空格分隔。此外,expr
的语法可能相对有些繁琐,并且它对于较复杂的数学和字符串操作可能不够强大。在现代的shell环境中,通常会使用双括号 ((...))
进行数学计算,以及其他更强大的命令和语法来处理字符串操作。
6、shell脚本
#!/bin/bash
read -p "请输入姓名:" NAME
read -p "请输入年龄:" AGE
read -p "请输入性别:" GENDER
stars=$(printf "%0.s*" {1..50})
cat <<EOF
$stars
姓名:$NAME
年龄:$AGE
性别: $GENDER
$stars
EOF
%0.s*
是一个格式化字符串,它在这里用于生成一系列重复的星号(*
)字符。让我来解释一下这个格式化字符串的各个部分:
-
%
:这是格式化字符串的起始标志。 -
0
:这是格式化字符串的宽度部分。在这里,它表示不添加额外的空格来填充。 -
.
:这表示精度部分的开始。 -
s
:这是一个转换说明符,表示将字符串传递到格式化字符串中。 -
*
:这是实际要传递到格式化字符串中的字符串,即星号字符。
在 {1..50}
的范围内,格式化字符串 %0.s*
将会被重复 50 次,生成一系列由星号组成的字符串,从而实现了重复的效果。
注意:这里有一个小误解,%0.s*
并没有 "."
,而是使用了 %0.s
来格式化输出一个空字符串,然后紧跟着 *
。
在 printf
的格式化字符串中,%
是一个特殊字符,用于指示格式控制符的开始。0
表示填充字符为零,.s
表示字符串的最大长度为 0(即空字符串),*
表示输出一个 *
字符。因此,printf "%0.s*" {1..50}
的作用是生成一个包含 50 个 *
字符的字符串,但不会代入 {1..50}
中的数字。
printf
和 print
都是用于在 shell 脚本中输出文本的命令,但它们在不同的上下文和环境中使用,并且存在一些区别:
-
语法和用法:
-
printf
:printf
是一个格式化输出命令,使用类似于 C 语言的格式字符串来定义输出的格式。它可以按照指定的格式输出文本,可以插入变量、控制字符等。 -
print
:print
是在某些 shell 中(如 Bash)用于输出文本的内建命令。它不支持像printf
那样的格式化字符串,只是简单地输出指定的文本。
-
-
格式化输出:
-
printf
:可以根据格式字符串进行复杂的格式化输出,允许插入变量、控制字符、对齐等。 -
print
:不支持格式化字符串,只能输出指定的字符串。
-
-
跨平台性:
-
printf
:在大多数 Unix-like 系统中都有,但具体实现可能会有些差异。 -
print
:不是所有 shell 都支持,它在某些 shell 中是可用的,但在其他 shell 中可能无法使用。
-
在 Bash 等支持 print
的 shell 中,你可以使用 print
命令来输出简单的文本,但如果需要进行格式化输出、插入变量等操作,通常会选择使用 printf
命令。在其他 shell 中,你可能只能使用 echo
或其他类似的命令来达到相似的输出效果
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· 清华大学推出第四讲使用 DeepSeek + DeepResearch 让科研像聊天一样简单!
· 实操Deepseek接入个人知识库
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库