一、Shell基础认知
(一)Shell是什么
Shell是用户与Linux内核交互的桥梁,它本质上是一个命令解释器,负责接收用户输入的命令,将其解析后传
递给内核执行,再把执行结果反馈给用户。同时,Shell也是一种脚本编程语言,支持变量、循环、条件判断
等编程特性,用户可以将一系列命令组合成脚本,实现自动化操作。
(二)常见Shell种类
Linux系统中存在多种Shell版本,常见的有:
Bourne Shell(sh):这是最早的Unix Shell,由贝尔实验室开发,语法简洁,是许多Shell的基础。
Bourne Again Shell(bash):GNU组织开发的免费Shell,是sh的增强版,兼容sh的所有功能,还增加
了命令补全、历史记录等实用特性,是目前大多数Linux发行版的默认Shell。
C Shell(csh):语法风格类似C语言,适合有C语言基础的用户,但目前使用范围逐渐缩小,多被其增强版tcsh取代。
Korn Shell(ksh):融合了sh和csh的优点,语法与sh兼容,同时具备csh的易用性,常用于一些安装脚本。
Z Shell(zsh):集成了bash、ksh等Shell的优秀特性,还增加了独特功能,功能强大但相对复杂,一般在特定场景使用。
可以通过cat /etc/shells命令查看系统支持的Shell种类,使用echo $SHELL命令查看当前使用的Shell。
二、第一个Shell脚本
(一)脚本编写
使用文本编辑器(如vi、vim)创建一个后缀为.sh的文件,例如test.sh,在文件中写入以下内容:
#!/bin/bash
# 这是第一个Shell脚本,输出Hello World
echo "Hello World!"
第一行
#!/bin/bash称为Shebang,用于指定脚本的解释器,告诉系统使用bash来执行该脚本。以
#开头的行是注释行,用于对脚本进行说明,不会被执行。echo命令用于输出内容到终端。
(二)脚本执行
脚本执行主要有以下几种方式:
赋予执行权限后直接执行:
首先使用
chmod +x test.sh命令给脚本添加执行权限。然后在当前目录下执行
./test.sh,这里的./表示当前目录,因为系统默认的命令搜索路径(PATH)通常不包含当前目录,所以需要明确指定路径。通过解释器执行:直接使用bash解释器执行脚本,无需赋予执行权限,命令为
bash test.sh或者sh test.sh(因为sh通常是bash的软链接)。在当前Shell环境执行:使用
source test.sh或者. test.sh命令,这种方式会在当前Shell进程中执行脚本,脚本中定义的变量等会保留在当前Shell环境中,而前两种方式会创建一个新的Shell进程来执行脚本。
三、Shell变量
(一)变量定义与使用
Shell是弱类型语言,变量定义时无需声明类型,直接赋值即可。变量名和等号之间不能有空格,命名规则如下:
只能使用英文字母、数字和下划线。
首个字符不能是数字。
不能使用Shell中的关键字(可通过
help命令查看保留关键字)。
示例:
# 定义变量
name="Linux Shell"
age=10
# 使用变量,在变量名前加$符号
echo "名称:$name"
echo "年龄:$age"
# 也可以使用${}来明确变量边界,避免歧义
echo "学习${name}编程"
(二)变量类型
局部变量:在脚本或命令中定义,仅在当前Shell实例中有效,其他Shell进程无法访问。例如在脚本中定义的变量,脚本执行结束后变量就会消失。
环境变量:可以在当前Shell及其子Shell中生效,甚至可以通过配置文件让其在所有Shell中生效。使用
export命令将局部变量转换为环境变量,示例:
export PATH=$PATH:/usr/local/new_bin
上述命令将/usr/local/new_bin目录添加到系统的PATH环境变量中,这样在终端中直接输入该目录下的命令即可执行,无需输入完整路径。常见的环境变量还有HOME(用户主目录)、USER(当前用户名)等,可以使用env命令查看所有环境变量。 3. 位置变量:用于获取执行脚本时传入的参数,$0表示脚本名称,$1表示第一个参数,$2表示第二个参数,以此类推,$#表示参数的个数,$*和$@表示所有参数(两者在不加引号时作用相同,加引号时$*会将所有参数作为一个整体,$@会将每个参数作为独立个体)。示例脚本args_test.sh:
#!/bin/bash
echo "脚本名称:$0"
echo "第一个参数:$1"
echo "第二个参数:$2"
echo "参数个数:$#"
echo "所有参数:$*"
执行./args_test.sh 苹果 香蕉,输出结果为:
脚本名称:./args_test.sh
第一个参数:苹果
第二个参数:香蕉
参数个数:2
所有参数:苹果 香蕉
预定义变量:Shell内置的一些特殊变量,例如
$?表示上一个命令的执行结果,返回0表示执行成功,非0表示执行失败;$$表示当前Shell进程的ID;$!表示上一个后台进程的ID。示例:
ls /nonexistent_directory
echo "上一个命令执行结果:$?" # 输出非0,因为目录不存在
echo "当前进程ID:$$"
(三)变量操作
只读变量:使用
readonly命令将变量设置为只读,只读变量的值不能被修改。示例:
readonly version="1.0"
version="2.0" # 执行此语句会报错,提示变量是只读的
删除变量:使用
unset命令删除变量,删除后变量将不再存在。示例:
city="Beijing"
unset city
echo $city # 输出为空,因为变量已被删除
四、Shell字符串操作
字符串是Shell编程中常用的数据类型,支持单引号、双引号和无引号三种表示方式。
(一)字符串表示
单引号:单引号中的内容会原样输出,不进行变量替换和转义。示例:
str='Hello $name'
echo $str # 输出Hello $name
双引号:双引号中的变量会被替换为对应的值,支持转义字符。示例:
name="Tom"
str="Hello $name\nWelcome to Shell world"
echo -e $str # -e参数用于启用转义字符,输出Hello Tom并换行,然后输出Welcome to Shell world
无引号:当字符串中没有空格、特殊字符时,可以使用无引号表示,但不推荐,容易出现歧义。
(二)字符串常用操作
字符串长度获取:使用
${#字符串名}获取字符串长度。示例:
str="Linux Shell"
echo "字符串长度:${#str}" # 输出11
字符串截取
从指定位置开始截取:
${字符串名:起始位置:长度},起始位置从0开始计数。示例:
str="Hello World"
echo ${str:0:5} # 输出Hello
- 从指定字符(或字符串)之后截取:`${字符串名#*字符}`(从左到右匹配第一个指定字符,截取其后面的内容),`${字符串名##*字符}`(从左到右匹配最后一个指定字符,截取其后面的内容)。示例:
str="https://www.example.com/path/file.html"
echo ${str#*://} # 输出www.example.com/path/file.html
echo ${str##*/} # 输出file.html
- 从指定字符(或字符串)之前截取:`${字符串名%字符*}`(从右到左匹配第一个指定字符,截取其前面的内容),`${字符串名%%字符*}`(从右到左匹配最后一个指定字符,截取其前面的内容)。示例:
str="https://www.example.com/path/file.html"
echo ${str%/*} # 输出https://www.example.com/path
echo ${str%%.*} # 输出https://www
字符串替换:
${字符串名/旧字符串/新字符串}(替换第一个匹配的旧字符串),${字符串名//旧字符串/新字符串}(替换所有匹配的旧字符串)。示例:
str="Hello Hello World"
echo ${str/Hello/Hi} # 输出Hi Hello World
echo ${str//Hello/Hi} # 输出Hi Hi World
五、Shell流程控制
(一)条件判断(if语句)
1. 基本if结构
if [ 条件表达式 ]; then
# 条件成立时执行的命令
fi
注意:[和条件表达式之间、条件表达式和]之间都要有空格。条件表达式可以进行文件测试、数值比较、字符串比较等。
文件测试:常见选项有
-e(文件存在)、-f(普通文件)、-d(目录)、-r(可读)、-w(可写)、-x(可执行)等。示例:
file="/etc/passwd"
if [ -f $file ]; then
echo "$file 是普通文件"
fi
数值比较:使用
-eq(等于)、-ne(不等于)、-gt(大于)、-lt(小于)、-ge(大于等于)、-le(小于等于)等选项。示例:
num1=10
num2=20
if [ $num1 -lt $num2 ]; then
echo "$num1 小于 $num2"
fi
字符串比较:使用
=(等于)、!=(不等于)、-z(字符串长度为0)、-n(字符串长度不为0)等选项。示例:
str1="abc"
str2="def"
if [ $str1 != $str2 ]; then
echo "$str1 和 $str2 不相等"
fi
2. if-else结构
if [ 条件表达式 ]; then
# 条件成立时执行的命令
else
# 条件不成立时执行的命令
fi
示例:
read -p "请输入一个数字:" num
if [ $num -gt 0 ]; then
echo "这是一个正数"
else
echo "这不是一个正数"
fi
3. if-elif-else结构
if [ 条件表达式1 ]; then
# 条件1成立时执行的命令
elif [ 条件表达式2 ]; then
# 条件2成立时执行的命令
else
# 所有条件都不成立时执行的命令
fi
示例:
read -p "请输入成绩:" score
if [ $score -ge 90 ]; then
echo "优秀"
elif [ $score -ge 80 ]; then
echo "良好"
elif [ $score -ge 60 ]; then
echo "及格"
else
echo "不及格"
fi
(二)循环语句
1. for循环
(1)列表遍历型
for 变量 in 列表
do
# 循环体命令
done
示例:遍历输出1到5的数字
for num in 1 2 3 4 5
do
echo $num
done
也可以使用通配符遍历文件,例如遍历当前目录下的所有.sh文件:
for file in *.sh
do
echo $file
done
(2)C语言风格型
for (( 初始化表达式; 条件表达式; 更新表达式 ))
do
# 循环体命令
done
示例:计算1到10的和
sum=0
for (( i=1; i<=10; i++ ))
do
sum=$((sum + i))
done
echo "1到10的和为:$sum"
2. while循环
while [ 条件表达式 ]
do
# 循环体命令
done
示例:输出1到5的数字
i=1
while [ $i -le 5 ]
do
echo $i
i=$((i + 1))
done
3. until循环
until循环与while循环相反,当条件不成立时执行循环体,条件成立时退出循环。
until [ 条件表达式 ]
do
# 循环体命令
done
示例:输出1到5的数字
i=1
until [ $i -gt 5 ]
do
echo $i
i=$((i + 1))
done
(三)case语句
case语句用于多分支判断,适合对一个变量进行多种情况的匹配。
case 变量 in
模式1)
# 匹配模式1时执行的命令
;;
模式2)
# 匹配模式2时执行的命令
;;
*)
# 匹配所有未匹配到的模式时执行的命令
;;
esac
示例:根据输入的数字输出对应的星期几
read -p "请输入1-7的数字:" num
case $num in
1)
echo "星期一"
;;
2)
echo "星期二"
;;
3)
echo "星期三"
;;
4)
echo "星期四"
;;
5)
echo "星期五"
;;
6)
echo "星期六"
;;
7)
echo "星期日"
;;
*)
echo "输入无效,请输入1-7的数字"
;;
esac
六、Shell函数
(一)函数定义
函数名() {
# 函数体命令
return 返回值 # 可选,返回值范围0-255,默认返回最后一条命令的执行结果
}
示例:定义一个计算两数之和的函数
add() {
sum=$(( $1 + $2 ))
echo $sum
}
(二)函数调用
直接使用函数名加参数的方式调用函数,参数通过位置变量$1、$2等在函数内部获取。示例:
result=$(add 10 20)
echo "两数之和为:$result"
(三)函数变量作用域
函数内部定义的变量默认是全局变量,在函数外部也可以访问。如果要定义局部变量,需要使用local关键字。示例:
test_func() {
local local_var="局部变量"
global_var="全局变量"
}
test_func
echo $global_var # 输出全局变量
echo $local_var # 输出为空,因为局部变量仅在函数内部有效
七、Shell脚本调试
在编写Shell脚本过程中,难免会出现错误,以下是一些常用的调试方法:
(一)使用-x参数调试
在执行脚本时添加-x参数,Shell会在执行每一条命令前,将命令及其参数输出到终端,方便查看执行流程。示例:
bash -x test.sh
也可以在脚本的Shebang行添加-x,例如#!/bin/bash -x,这样每次执行脚本都会自动开启调试模式。
(二)使用-n参数检查语法
-n参数可以检查脚本的语法错误,而不实际执行脚本。示例:
bash -n test.sh
(三)添加调试输出
在脚本中关键位置添加echo命令,输出变量的值或执行状态,帮助定位问题。例如:
num=10
echo "当前num的值为:$num"