Shell脚本和编程
- Linux服务器的基本操作和管理
- 前端nodejs服务的进程管理,问题排查,资源监控等运维操作
- 使用shell编写tce、scm、docker脚本,完成服务编译和部署
shell基础概念
Shell是一种在操作系统上运行的命令解释器,它是一个命令行界面,允许用户输入命令并运行对应的程序。Shell常被用于管理操作系统,例如文件操作、进程管理、系统配置等。在Linux和Unix操作系统中,有许多不同的Shell可供选择,例如bash、zsh、ksh等。
命令行界面:Shell提供了一个命令行界面,允许用户输入命令并运行相应的程序。
Shell脚本:Shell脚本是一系列Shell命令的集合,可以被保存为脚本文件,并在需要时运行。脚本文件通常以.sh作为文件扩展名。
变量:在Shell中,可以定义和使用变量来存储数据。变量以$符号开头,例如$var,可以用来存储字符串、数字等类型的数据。
条件语句:Shell中支持if、else、elif等条件语句,用于根据条件来执行不同的命令或代码块。
循环结构:Shell中支持for、while、until等循环结构,可以用来迭代执行一系列命令或代码块。
输入输出重定向:Shell中可以使用输入输出重定向来改变命令的输入和输出。例如,将一个命令的输出重定向到一个文件中。
管道操作:Shell中可以使用管道符号(|)来将一个命令的输出作为另一个命令的输入。例如,将一个命令的输出传递给另一个命令用作输入。
shell构成
解释器
- bash内置指令
- gun核心工具库
- 第三方库
编程语言
- 变量
- 自定义变量
- 环境变量
- 系统环境变量
- 运算
- 逻辑运算
- 算数运算
- 语句
- 判断
- 分支
- 循环
- 函数
配置文件加载
运算符和引用
管道
在Shell中,管道(|)是一种用于将一个命令的输出作为另一个命令的输入的机制。管道将两个或多个命令连接在一起,使得它们可以协同工作来完成更复杂的任务。管道可以大大简化命令的编写和处理。以下是一些管道的示例:
- 用管道将两个命令连接在一起,例如:
ls -l | grep ".txt"
这个命令列出当前目录的所有文件和子目录,然后将结果作为输入送到grep命令中,以筛选出以".txt"结尾的文件。
- 管道可以用于连接多个命令。例如,以下命令使用管道连接三个命令:
cat file.txt | awk '{print $2}' | sort | uniq
这个命令读取一个名为file.txt的文件,并将其传递给awk命令。awk然后提取每行中的第二个单词,并将其传递给sort命令进行排序,并且最终将结果传递给uniq命令,以去除重复的行。
- 管道可以用于将命令的输出直接写入文件中。例如,以下命令将一个查询结果的输出重定向到文件中:
ps -ef | grep "firefox" > firefox_processes.txt
这个命令运行ps命令来列出进程,然后使用grep命令来查找包含"firefox"的进程。最后,将输出写入名为firefox_processes.txt的文件中。
管道是Shell中一个非常有用的功能,可以让Shell命令更加灵活和强大。管道不仅可以连接两个命令,还可以连接多个命令和处理复杂的数据。
重定向
在Shell中,重定向是用于改变命令输入和输出的一种简便方法。通过重定向,可以将命令的输入或输出重定向到文件、其他命令或特殊设备中。以下是一些常见的重定向操作:
- 输出重定向
使用>符号可以将命令的输出重定向到文件中:
ls > file.txt
这个命令将当前目录的文件列表输出到file.txt文件中,如果文件不存在会创建一个新文件,如果文件存在则清空文件内容。
使用>>符号可以将命令输出附加到文件的末尾,而不是替换文件中的内容:
echo "新内容" >> file.txt
这个命令将"新内容"追加到file.txt文件的末尾。
- 输入重定向
使用<符号可以将命令从文件中读取输入:
sort < file.txt
这个命令将file.txt文件作为输入,按字母顺序对其进行排序并输出结果。
- 管道重定向
使用|符号将一个命令的输出作为另一个命令的输入,管道重定向如前所述。
- /dev/null
/dev/null是一个特殊的设备文件,可以用来丢弃命令的输出或输入。例如,以下命令会运行命令,但在屏幕上不显示任何输出:
command > /dev/null
这个命令将命令的输出重定向到/dev/null设备文件中,从而将命令输出丢弃。
这些是一些常见的Shell重定向操作。重定向是Unix / Linux命令行中非常有用的一项技术,可以使命令变得更加灵活和强大。
判断命令
在Shell脚本中,可以使用if语句来判断命令是否执行成功或某个条件是否为真。以下是一些常见的Shell判断命令:
- 当命令执行成功时返回0,可以使用
$?特殊变量来检查命令是否执行成功。
command
if [ $? -eq 0 ]; then
echo "命令执行成功"
else
echo "命令执行失败"
fi
上述示例中,如果command执行成功,则输出"命令执行成功",否则输出"命令执行失败"。
- 可以使用
-z选项来检查字符串是否为空。
str=""
if [ -z "$str" ]; then
echo "字符串为空"
else
echo "字符串不为空"
fi
以上示例中,如果str为空字符串,则输出"字符串为空",否则输出"字符串不为空"。
- 可以使用
-eq、-ne、-lt、-le、-gt、-ge等选项来比较数字。
num1=10
num2=20
if [ $num1 -eq $num2 ]; then
echo "两个数字相等"
elif [ $num1 -lt $num2 ]; then
echo "num1小于num2"
else
echo "num1大于num2"
fi
上述示例中,如果num1等于num2,则输出"两个数字相等";否则,如果num1小于num2,则输出"num1小于num2";否则,输出"num1大于num2"。
这些是一些常见的Shell判断命令。在Shell脚本中使用if语句可以根据条件来控制程序的执行流程,从而使Shell脚本更加灵活。
分支语句
在Shell脚本中,可以使用分支语句来根据条件执行不同的操作。Shell中最常用的分支语句是if-else语句和case语句。
if-else语句
if-else语句用于根据条件执行不同的操作。以下是if-else语句的基本语法:
if [ condition ]
then
# code to be executed if the condition is true
else
# code to be executed if the condition is false
fi
其中,condition是要检查的条件表达式。如果condition为真,则执行then中的代码;否则执行else中的代码。
以下是一个经典的if-else语句示例,用于判断数字大小:
num1=10
num2=20
if [ $num1 -gt $num2 ]
then
echo "$num1 大于 $num2"
else
echo "$num1 小于等于 $num2"
fi
case语句
case语句类似于switch-case语句,用于根据变量的值执行不同的操作。以下是case语句的基本语法:
case variable in
pattern1)
# code to be executed when variable matches pattern1
;;
pattern2)
# code to be executed when variable matches pattern2
;;
patternN)
# code to be executed when variable matches patternN
;;
*)
# code to be executed when variable does not match any pattern
;;
esac
其中,variable是要检查的变量,pattern1..N是匹配的模式,*是一个通配符,匹配任何值。
以下是一个简单的case语句示例:
fruit=apple
case $fruit in
"apple") echo "这是苹果" ;;
"banana") echo "这是香蕉" ;;
"orange") echo "这是橘子" ;;
*) echo "未知的水果" ;;
esac
这个示例首先检查变量$fruit的值,如果值等于apple,则输出"这是苹果";如果值等于banana,则输出"这是香蕉";如果值等于orange,则输出"这是橘子";否则,输出"未知的水果"。
函数
Shell 函数是一个独立的代码块,在脚本中可以被多次调用。创建函数是一种将代码块组织为逻辑单元的方式,可以提高代码的可读性和可维护性。在 Shell 中,函数的语法与普通命令的语法非常相似,可以在需要的地方处于任何位置定义和调用。
下面是一个简单的 Shell 函数示例,该函数输出传递给它的两个字符串参数:
# 定义函数
myfunc() {
echo "Hello $1, nice to meet you, $2!"
}
# 调用函数
myfunc "Alice" "how are you doing today?"
在调用该脚本时,该函数将输出字符串 "Hello Alice, nice to meet you, how are you doing today?!"。
模块化
Shell 脚本的模块化可以通过将公共代码段提取到单独的文件中,然后在需要的脚本中引用这些文件来实现。这种方法可以提高代码的可重用性和可维护性,使代码更易于组织和管理。
下面是一种常见的 Shell 模块化技术,使用 .sh 文件作为模块文件,并使用 source 命令在需要的脚本中引用模块文件。以调用其他脚本中的函数为例,可以按照以下步骤进行:
1.在一个 .sh 文件中定义需要共享的函数或变量,例如:
# file1.sh
function hello() {
echo "Hello, world!"
}
export MYVAR="I am a variable"
2.在要使用这些函数或变量的脚本中使用 source 或者 . 命令引入定义好的模块文件,例如:
# file2.sh
source file1.sh
hello
echo $MYVAR
在执行 file2.sh 脚本时,会输出 "Hello, world!" 和 "I am a variable"。
这种方法可以用于拆分大型脚本文件,或将公共代码存储在集中的位置以便于管理和调用。同时,它也可以使代码更加易于重用和维护。
执行过程和原理
执行过程
Shell 脚本是一组命令的集合,按顺序执行。当我们运行一个 Shell 脚本时,它将被 Shell 解释器读入,并一行一行地执行,直到执行完毕或遇到错误。
Shell 执行脚本的过程如下:
-
在终端或其他调用程序中输入脚本名称,Shell 解释器将启动并读入脚本文件。
-
Shell 解释器读入脚本文件的第一行内容,判断该行是否以
#!开头。如果是,将其视为 "shebang" 行,指定该脚本的解释器。 -
解释器将读取脚本中的命令,并执行它们。每个命令通常由一个或多个单词组成,其中第一个单词是命令本身,其余单词为该命令所需的任何参数。
-
命令被解释器执行,它们可能会被执行系统调用调用底层系统功能,例如创建进程、读取文件、输出到终端等等。
-
当脚本中的所有命令都被执行完成后,Shell 解释器结束。
-
如果存在错误或故障,解释器将返回错误代码以指示问题所在。
-
脚本执行过程中,使用
echo命令或者将值赋给变量等方式输出结果。
需要注意的是,Shell 脚本的执行过程可以被中断或暂停,例如使用 ctrl+c 终止脚本执行,或者使用 ctrl+z 将脚本置于后台运行等。
shell展开
Shell 展开是指将特定字符或通配符转换为表达式、路径或文件名的过程。例如,通配符 * 被展开为当前目录下所有文件名的列表,而环境变量 $HOME 被展开为当前用户的主目录路径。
Shell 展开包括:
- 命令替换:将命令的输出插入到其他命令或字符串中。用反引号或
$()包围一个命令来进行命令替换。
例如:
echo "The current date is $(date +%Y-%m-%d)"
输出为:“The current date is 2022-07-15”。
- 变量替换:将变量名替换为其值。用
$符号加上变量名来进行变量替换。
例如:
MYVAR="Hello, world!"
echo "The message is: $MYVAR"
输出为:“The message is: Hello, world!”。
- 通配符展开:将通配符
*展开为当前目录下所有匹配的文件名。
例如:
ls *.txt
将输出所有扩展名为 .txt 的文件名。
- 大括号展开:用花括号
{ }将一组值括在一起,形成一个列表,通过逗号分隔。展开时,每个值被顺序替换到大括号的位置。
例如:
echo {one,two,three}
将输出: "one two three" 。
需要注意的是,在展开中使用特殊字符和通配符时,应特别小心以避免不必要的结果。必要时需要使用引号或转义字符对特殊字符进行保护。
调试和前端集成
调试
- 普通log,使用echo、printf
- 使用set命令
- vscode debug插件
vscode配置
- shellman:代码提示和自动补全
- shellcheck:代码语法校验
- shell-format:代码格式化
- Bash Debug:支持单步调试
- 安装vscode插件
- 编写launch.json文件
- 升级bash到4.x以上版本
个人总结
了解从其发展历史、基础语法开始,通过示例逐步深入, 学会使用 Shell ,也在一定程度上去理解 Shell 的执行原理和语法设计。