Shell是Linux用户接触最多的东西。每天打开终端输入命令查看文件启动程序都是在和Shell打交道。

但是很多人对Shell的了解只停留在会输入几个常用命令。比如ls、cd、pwd、mkdir、rm、cp、mv、cat、grep、find等。对于Shell脚本编程却知之甚少。

其实Shell脚本是Linux下最强大也最实用的工具之一。通过Shell脚本可以把一系列命令组合起来实现自动化操作。无论是批量处理文件还是定时备份数据还是监控系统状态还是部署应用程序Shell脚本都能大显身手。

作为一个后端开发和运维爱好者我每天都在和Shell脚本打交道。今天就来分享一下Shell脚本编程的入门知识和实用技巧。希望能帮初学者快速上手Shell脚本编程。

什么是Shell脚本

Shell脚本简单来说就是把一系列Shell命令写在一个文本文件里然后一次性执行。

比如我们每天都要执行以下操作:

  1. 进入项目目录
  2. 更新代码
  3. 安装依赖
  4. 构建项目
  5. 重启服务

如果每次都手动输入这5条命令不仅麻烦而且容易出错。如果把这些命令写在一个Shell脚本里每次只需要执行这个脚本就可以了。

这就是Shell脚本的价值:自动化重复性操作提高效率减少错误。

Shell脚本的文件通常以,.sh,为后缀。比如,deploy.shbackup.shstart.sh等。

第一个Shell脚本

让我们来写第一个Shell脚本,hello.sh

#!/bin/bash
# 这是我的第一个Shell脚本
echo "Hello, World!"
echo "今天是 $(date)"
echo "当前用户是 $USER"
echo "当前目录是 $PWD"

让我们逐行解释:

  1. #!/bin/bash:这是脚本的第一行称为,"shebang"。它告诉系统用哪个解释器来执行这个脚本。这里指定的是,/bin/bash,也就是Bash Shell。
  2. # 这是我的第一个Shell脚本:以,#,开头的行是注释。注释不会被执行只是用来说明代码的作用。
  3. echo "Hello, World!"echo,命令用于输出文本。这里输出,"Hello, World!"。
  4. echo "今天是 $(date)"$(date),是命令替换。它会执行,date,命令然后把命令的输出替换到这里。
  5. echo "当前用户是 $USER"$USER,是环境变量。它存储了当前登录的用户名。
  6. echo "当前目录是 $PWD"$PWD,是环境变量。它存储了当前所在的目录。

执行脚本

写好脚本后需要给脚本添加执行权限然后才能执行:

# 添加执行权限
chmod +x hello.sh

# 执行脚本
./hello.sh

输出应该类似这样:

Hello, World!
今天是 2015年 11月 23日 星期一 10:00:00 CST
当前用户是 ubuntu
当前目录是 /home/ubuntu

当然也可以不加执行权限直接用bash来执行:

bash hello.sh

Shell脚本基础语法

变量

Shell脚本中定义变量很简单:

# 定义变量
name="张三"
age=25
email="zhangsan@example.com"

# 使用变量
echo "姓名:$name"
echo "年龄:$age"
echo "邮箱:$email"

注意:

  • 变量名和等号之间不能有空格。name = "张三",是错误的。
  • 使用变量时需要在变量名前加,$
  • 变量名区分大小写。name,和,NAME,是两个不同的变量。

读取用户输入

使用,read,命令可以读取用户的输入:

#!/bin/bash
echo "请输入你的名字:"
read name
echo "你好,$name!"

条件判断

Shell脚本中使用,if,语句进行条件判断:

#!/bin/bash
echo "请输入一个数字:"
read num

if [ $num -gt 10 ]; then
    echo "这个数字大于10"
elif [ $num -eq 10 ]; then
    echo "这个数字等于10"
else
    echo "这个数字小于10"
fi

常用的比较运算符:

  • -gt:大于(greater than)
  • -lt:小于(less than)
  • -ge:大于等于(greater or equal)
  • -le:小于等于(less or equal)
  • -eq:等于(equal)
  • -ne:不等于(not equal)

字符串比较:

  • =:等于
  • !=:不等于
  • -z:字符串为空
  • -n:字符串不为空

文件测试:

  • -f:文件存在且是普通文件
  • -d:文件存在且是目录
  • -r:文件存在且可读
  • -w:文件存在且可写
  • -x:文件存在且可执行
  • -e:文件存在

循环

Shell脚本中有,for,循环和,while,循环。

for循环:

#!/bin/bash
# 遍历列表
for i in 1 2 3 4 5; do
    echo "数字:$i"
done

# 遍历文件
for file in *.txt; do
    echo "文件:$file"
done

# C风格for循环
for ((i=1; i<=5; i++)); do
    echo "数字:$i"
done

while循环:

#!/bin/bash
i=1
while [ $i -le 5 ]; do
    echo "数字:$i"
    i=$((i + 1))
done

函数

Shell脚本中可以定义函数:

#!/bin/bash
# 定义函数
greet() {
    echo "你好,$1!"
}

# 调用函数
greet "张三"
greet "李四"

# 带返回值的函数
add() {
    return $(( $1 + $2 ))
}

add 3 5
echo "3 + 5 = $?"

注意:

  • 函数参数通过,$1$2$3,等来获取。
  • 函数返回值通过,return,语句返回只能返回0-255的整数。
  • 调用函数后通过,$?,获取返回值。

数组

Bash支持数组:

#!/bin/bash
# 定义数组
fruits=("苹果" "香蕉" "橙子" "葡萄")

# 访问数组元素
echo "第一个水果:${fruits[0]}"
echo "第二个水果:${fruits[1]}"

# 遍历数组
for fruit in "${fruits[@]}"; do
    echo "水果:$fruit"
done

# 获取数组长度
echo "水果总数:${#fruits[@]}"

实用Shell脚本示例

下面分享几个实用的Shell脚本示例帮助大家理解Shell脚本的用法。

1. 批量重命名文件

#!/bin/bash
# 批量将当前目录下的.txt文件重命名为.txt.bak
for file in *.txt; do
    if [ -f "$file" ]; then
        mv "$file" "${file}.bak"
        echo "已重命名:$file -> ${file}.bak"
    fi
done

2. 备份数据库

#!/bin/bash
# MySQL数据库备份脚本
DB_USER="root"
DB_PASS="password"
DB_NAME="mydb"
BACKUP_DIR="/backup/mysql"
DATE=$(date +%Y%m%d_%H%M%S)

# 创建备份目录
mkdir -p "$BACKUP_DIR"

# 执行备份
mysqldump -u "$DB_USER" -p"$DB_PASS" "$DB_NAME" > "$BACKUP_DIR/${DB_NAME}_${DATE}.sql"

# 压缩备份文件
gzip "$BACKUP_DIR/${DB_NAME}_${DATE}.sql"

# 删除7天前的备份
find "$BACKUP_DIR" -name "*.sql.gz" -mtime +7 -delete

echo "数据库备份完成:$BACKUP_DIR/${DB_NAME}_${DATE}.sql.gz"

3. 监控磁盘空间

#!/bin/bash
# 磁盘空间监控脚本
THRESHOLD=80
EMAIL="admin@example.com"

# 获取根分区使用率
USAGE=$(df / | grep / | awk '{print $5}' | sed 's/%//g')

if [ "$USAGE" -gt "$THRESHOLD" ]; then
    echo "警告:根分区使用率已达 ${USAGE}%,超过阈值 ${THRESHOLD}%"
    # 发送邮件告警
    echo "根分区使用率已达 ${USAGE}%,请及时清理!" | mail -s "磁盘空间告警" "$EMAIL"
else
    echo "根分区使用率:${USAGE}%,正常"
fi

4. 自动部署项目

#!/bin/bash
# 项目自动部署脚本
PROJECT_DIR="/var/www/myproject"
GIT_REPO="git@github.com:user/myproject.git"
BRANCH="master"

echo "开始部署..."

# 进入项目目录
cd "$PROJECT_DIR" || exit 1

# 拉取最新代码
echo "拉取最新代码..."
git pull "$GIT_REPO" "$BRANCH"

# 安装依赖
echo "安装依赖..."
composer install --no-dev --optimize-autoloader

# 执行数据库迁移
echo "执行数据库迁移..."
php artisan migrate --force

# 清除缓存
echo "清除缓存..."
php artisan cache:clear
php artisan config:cache
php artisan route:cache

# 重启服务
echo "重启服务..."
service php-fpm restart
service nginx reload

echo "部署完成!"

5. 统计代码行数

#!/bin/bash
# 统计项目代码行数
PROJECT_DIR="/var/www/myproject"

echo "代码行数统计:"
echo "=============================="

# 统计PHP文件行数
php_lines=$(find "$PROJECT_DIR" -name "*.php" -exec cat {} \; | wc -l)
echo "PHP代码行数:$php_lines"

# 统计JS文件行数
js_lines=$(find "$PROJECT_DIR" -name "*.js" -exec cat {} \; | wc -l)
echo "JS代码行数:$js_lines"

# 统计CSS文件行数
css_lines=$(find "$PROJECT_DIR" -name "*.css" -exec cat {} \; | wc -l)
echo "CSS代码行数:$css_lines"

# 统计HTML文件行数
html_lines=$(find "$PROJECT_DIR" -name "*.html" -exec cat {} \; | wc -l)
echo "HTML代码行数:$html_lines"

echo "=============================="
total=$((php_lines + js_lines + css_lines + html_lines))
echo "总代码行数:$total"

Shell脚本最佳实践

1. 添加注释

好的Shell脚本应该有清晰的注释。说明脚本的作用参数返回值,等。这样别人或者以后的自己才能看懂脚本。

#!/bin/bash
# 脚本名称:backup.sh
# 脚本功能:备份MySQL数据库
# 创建时间:2015-11-23
# 作者:张三
# 使用方法:./backup.sh [数据库名]

2. 使用有意义的变量名

变量名应该有意义让人一看就知道这个变量是干什么的。

# 不好的写法
a="/backup/mysql"
b="root"
c="password"

# 好的写法
BACKUP_DIR="/backup/mysql"
DB_USER="root"
DB_PASS="password"

3. 引用变量时加双引号

引用变量时最好加上双引号防止变量值中包含空格导致错误。

# 不好的写法
rm $file

# 好的写法
rm "$file"

4. 使用set -e

在脚本开头加上,set -e,可以让脚本在遇到错误时立即退出避免错误继续传播。

#!/bin/bash
set -e
# 脚本内容...

5. 检查命令是否成功

重要的命令执行后应该检查是否成功。

if ! mysqldump -u "$DB_USER" -p"$DB_PASS" "$DB_NAME" > "$BACKUP_FILE"; then
    echo "数据库备份失败!"
    exit 1
fi

6. 使用函数封装重复代码

如果有重复的代码应该封装成函数提高代码的复用性和可读性。

log() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1"
}

log "开始备份"
log "备份完成"

7. 测试脚本

脚本写好后一定要充分测试。可以先在测试环境测试确认没问题再在生产环境使用。

学习资源

推荐书籍

  • 《Linux命令行与Shell脚本编程大全》:非常经典的Shell脚本入门书籍内容全面适合初学者。
  • 《Shell脚本学习指南》:O'Reilly出版的Shell脚本书籍内容深入适合有一定基础的读者。
  • 《Bash Cookbook》:Bash脚本的食谱包含大量实用的脚本示例。

在线教程

  • Bash Guide for Beginners:https://tldp.org/LDP/Bash-Beginners-Guide/html/
  • Advanced Bash-Scripting Guide:https://tldp.org/LDP/abs/html/
  • Shell Scripting Tutorial:https://www.shellscript.sh/
  • Bash Academy:https://guide.bash.academy/

工具

  • ShellCheck:https://www.shellcheck.net/ ,Shell脚本静态分析工具可以帮你发现脚本中的错误和潜在问题。
  • explainshell:https://explainshell.com/ ,输入命令可以解释命令的每个参数的含义。

写在最后

Shell脚本是Linux用户必备的技能之一。无论是开发运维还是测试掌握Shell脚本都能大大提高你的工作效率。

学习Shell脚本不要只停留在理论上。一定要多写多练多用。从简单的脚本开始慢慢尝试复杂的脚本。遇到问题多查文档多搜索多思考。

Shell脚本虽然语法简单但功能强大。它能帮你自动化各种重复性的操作让你从繁琐的工作中解放出来专注于更有价值的事情。

2015年DevOps越来越火自动化成为了趋势。Shell脚本作为自动化的基础工具更是不可或缺。希望这篇文章能帮初学者快速入门Shell脚本编程也希望大家都能用好Shell脚本提高工作效率。

"工欲善其事必先利其器。" Shell脚本就是Linux用户最好的工具之一。掌握它你会发现原来很多繁琐的工作都可以变得如此简单。

祝大家在Shell脚本的世界里游刃有余写出高效优雅的脚本!