河内机器人 Linux Shell编程入门教程


一、Shell基础认知

(一)Shell是什么

Shell是用户与Linux内核交互的桥梁,它本质上是一个命令解释器,负责接收用户输入的命令,将其解析后传

递给内核执行,再把执行结果反馈给用户。同时,Shell也是一种脚本编程语言,支持变量、循环、条件判断

等编程特性,用户可以将一系列命令组合成脚本,实现自动化操作。

(二)常见Shell种类

Linux系统中存在多种Shell版本,常见的有:

  1. Bourne Shell(sh):这是最早的Unix Shell,由贝尔实验室开发,语法简洁,是许多Shell的基础。

  2. Bourne Again Shell(bash):GNU组织开发的免费Shell,是sh的增强版,兼容sh的所有功能,还增加

  3. 了命令补全、历史记录等实用特性,是目前大多数Linux发行版的默认Shell。

  4. C Shell(csh):语法风格类似C语言,适合有C语言基础的用户,但目前使用范围逐渐缩小,多被其增强版tcsh取代。

  5. Korn Shell(ksh):融合了sh和csh的优点,语法与sh兼容,同时具备csh的易用性,常用于一些安装脚本。

  6. 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命令用于输出内容到终端。

(二)脚本执行

脚本执行主要有以下几种方式:

  1. 赋予执行权限后直接执行

    • 首先使用chmod +x test.sh命令给脚本添加执行权限。

    • 然后在当前目录下执行./test.sh,这里的./表示当前目录,因为系统默认的命令搜索路径(PATH)通常不包含当前目录,所以需要明确指定路径。

  2. 通过解释器执行:直接使用bash解释器执行脚本,无需赋予执行权限,命令为bash test.sh或者sh test.sh(因为sh通常是bash的软链接)。

  3. 在当前Shell环境执行:使用source test.sh或者. test.sh命令,这种方式会在当前Shell进程中执行脚本,脚本中定义的变量等会保留在当前Shell环境中,而前两种方式会创建一个新的Shell进程来执行脚本。

三、Shell变量

(一)变量定义与使用

Shell是弱类型语言,变量定义时无需声明类型,直接赋值即可。变量名和等号之间不能有空格,命名规则如下:

  • 只能使用英文字母、数字和下划线。

  • 首个字符不能是数字。

  • 不能使用Shell中的关键字(可通过help命令查看保留关键字)。

示例:

# 定义变量
name="Linux Shell"
age=10

# 使用变量,在变量名前加$符号
echo "名称:$name"
echo "年龄:$age"

# 也可以使用${}来明确变量边界,避免歧义
echo "学习${name}编程"

(二)变量类型

  1. 局部变量:在脚本或命令中定义,仅在当前Shell实例中有效,其他Shell进程无法访问。例如在脚本中定义的变量,脚本执行结束后变量就会消失。

  2. 环境变量:可以在当前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
所有参数:苹果 香蕉

  1. 预定义变量:Shell内置的一些特殊变量,例如$?表示上一个命令的执行结果,返回0表示执行成功,非0表示执行失败;$$表示当前Shell进程的ID;$!表示上一个后台进程的ID。示例:

ls /nonexistent_directory
echo "上一个命令执行结果:$?"  # 输出非0,因为目录不存在
echo "当前进程ID:$$"

(三)变量操作

  1. 只读变量:使用readonly命令将变量设置为只读,只读变量的值不能被修改。示例:

readonly version="1.0"
version="2.0"  # 执行此语句会报错,提示变量是只读的

  1. 删除变量:使用unset命令删除变量,删除后变量将不再存在。示例:

city="Beijing"
unset city
echo $city  # 输出为空,因为变量已被删除

四、Shell字符串操作

字符串是Shell编程中常用的数据类型,支持单引号、双引号和无引号三种表示方式。

(一)字符串表示

  1. 单引号:单引号中的内容会原样输出,不进行变量替换和转义。示例:

str='Hello $name'
echo $str  # 输出Hello $name

  1. 双引号:双引号中的变量会被替换为对应的值,支持转义字符。示例:

name="Tom"
str="Hello $name\nWelcome to Shell world"
echo -e $str  # -e参数用于启用转义字符,输出Hello Tom并换行,然后输出Welcome to Shell world

  1. 无引号:当字符串中没有空格、特殊字符时,可以使用无引号表示,但不推荐,容易出现歧义。

(二)字符串常用操作

  1. 字符串长度获取:使用${#字符串名}获取字符串长度。示例:

str="Linux Shell"
echo "字符串长度:${#str}"  # 输出11

  1. 字符串截取

    • 从指定位置开始截取:${字符串名:起始位置:长度},起始位置从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

  1. 字符串替换${字符串名/旧字符串/新字符串}(替换第一个匹配的旧字符串),${字符串名//旧字符串/新字符串}(替换所有匹配的旧字符串)。示例:

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"