1. 脚本编程基础概述
在日常工作中,脚本语言就像一把瑞士军刀,能帮我们快速解决各种重复性任务。无论是系统管理、数据处理还是自动化流程,掌握常用脚本语句都能显著提升工作效率。我使用脚本语言已有十余年,从最初的简单批处理到现在能处理复杂业务逻辑,积累了不少实用经验。
脚本语言之所以受欢迎,主要因为它的即时性和灵活性。不需要编译过程,写完就能运行;语法通常比传统编程语言更简洁;而且大多数操作系统都内置了脚本解释器。常见的脚本语言包括Bash、Python、PowerShell等,它们各有特点但核心思想相通。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 基础语句结构解析
2.1 变量定义与使用
变量是脚本中最基础的元素。在Bash中定义变量很简单:
bash复制username="JohnDoe"
count=42
但有几个细节需要注意:
- 等号两边不能有空格
- 默认都是字符串类型,即使你写了数字
- 使用变量时要加$符号:
echo $username
Python的变量更灵活:
python复制username = "JohnDoe" # 字符串
count = 42 # 整数
price = 3.14 # 浮点数
is_valid = True # 布尔值
重要提示:Bash中变量作用域很特殊。默认情况下在函数内定义的变量是全局的,除非用local声明:
local inner_var="value"
2.2 条件判断语句
条件判断让脚本有了决策能力。Bash中使用if语句:
bash复制if [ -f "/path/to/file" ]; then
echo "文件存在"
elif [ -d "/path/to/dir" ]; then
echo "目录存在"
else
echo "都不存在"
fi
Python的if语句更直观:
python复制if os.path.isfile("/path/to/file"):
print("文件存在")
elif os.path.isdir("/path/to/dir"):
print("目录存在")
else:
print("都不存在")
常见判断条件:
- 文件判断:-e(存在)、-f(文件)、-d(目录)
- 字符串比较:=、!=、-z(空)
- 数值比较:-eq、-ne、-gt、-lt
3. 循环控制语句实战
3.1 for循环应用场景
处理批量任务时for循环必不可少。Bash中遍历列表:
bash复制for fruit in apple orange banana; do
echo "当前水果: $fruit"
done
遍历数字序列:
bash复制for i in {1..5}; do
echo "第$i次循环"
done
Python的for循环更强大:
python复制for i in range(1, 6): # 1到5
print(f"第{i}次循环")
items = ["apple", "orange", "banana"]
for index, value in enumerate(items):
print(f"索引{index}: {value}")
3.2 while循环与退出控制
当循环次数不确定时,while循环更合适:
bash复制count=0
while [ $count -lt 5 ]; do
echo "计数: $count"
((count++))
done
Python中使用while True配合break更灵活:
python复制import random
while True:
num = random.randint(1, 100)
print(f"生成数字: {num}")
if num > 90:
print("找到大于90的数")
break
循环控制语句:
- break:立即退出循环
- continue:跳过本次迭代
- exit:直接退出脚本
4. 函数定义与参数传递
4.1 基础函数编写
Bash中定义函数:
bash复制greet() {
local name=$1
echo "Hello, $name!"
return 0
}
greet "Alice"
Python函数功能更丰富:
python复制def greet(name, times=1):
"""打招呼函数
Args:
name: 人名
times: 重复次数
Returns:
拼接的问候语
"""
message = " ".join([f"Hello {name}!"] * times)
return message
print(greet("Bob", 3))
4.2 参数处理技巧
Bash脚本获取参数:
bash复制#!/bin/bash
# $0 脚本名
# $1 第一个参数
# $# 参数个数
# $@ 所有参数
echo "执行脚本: $0"
echo "参数个数: $#"
echo "所有参数: $@"
Python使用argparse处理复杂参数:
python复制import argparse
parser = argparse.ArgumentParser()
parser.add_argument("-u", "--user", required=True)
parser.add_argument("-v", "--verbose", action="store_true")
args = parser.parse_args()
if args.verbose:
print(f"正在处理用户: {args.user}")
5. 实用脚本语句集锦
5.1 文件操作语句
检查文件存在:
bash复制if [ ! -f "data.txt" ]; then
touch "data.txt"
fi
Python读写文件更安全的方式:
python复制try:
with open("data.txt", "r") as f:
content = f.read()
except FileNotFoundError:
with open("data.txt", "w") as f:
f.write("初始内容")
5.2 字符串处理
Bash字符串操作:
bash复制filename="archive.tar.gz"
echo "主文件名: ${filename%%.*}" # archive
echo "扩展名: ${filename##*.}" # gz
Python字符串方法更丰富:
python复制text = " Hello World "
print(text.strip()) # "Hello World"
print(text.replace("l", "L")) # " HeLLo WorLd "
print("apple,orange".split(",")) # ['apple', 'orange']
5.3 日期时间处理
获取当前时间:
bash复制current_date=$(date +"%Y-%m-%d")
echo "今天是 $current_date"
Python的datetime模块:
python复制from datetime import datetime, timedelta
now = datetime.now()
print(f"当前时间: {now.strftime('%Y-%m-%d %H:%M:%S')}")
tomorrow = now + timedelta(days=1)
print(f"明天日期: {tomorrow.date()}")
6. 调试与错误处理
6.1 调试技巧
Bash调试模式:
bash复制#!/bin/bash -x # 开启调试模式
set -e # 遇到错误立即退出
set -o pipefail # 管道命令失败也退出
Python的pdb调试器:
python复制import pdb
def complex_calculation(a, b):
pdb.set_trace() # 设置断点
result = a * b + (a - b)
return result
6.2 错误捕获与处理
Bash错误处理:
bash复制if ! command_that_might_fail; then
echo "命令执行失败" >&2
exit 1
fi
Python的try-except:
python复制try:
risky_operation()
except FileNotFoundError as e:
print(f"文件未找到: {e}")
except Exception as e:
print(f"未知错误: {e}")
finally:
cleanup_resources()
7. 性能优化技巧
7.1 减少子进程调用
Bash中避免频繁调用外部命令:
bash复制# 不好: 每次循环都调用grep
for file in *; do
if grep -q "pattern" "$file"; then
echo "$file"
fi
done
# 更好: 使用内置字符串匹配
for file in *; do
if [[ "$(<"$file")" == *"pattern"* ]]; then
echo "$file"
fi
done
7.2 使用高效数据结构
Python中选择合适的数据结构:
python复制# 列表 vs 集合查找
names = ["Alice", "Bob", "Charlie"] # O(n)查找
name_set = {"Alice", "Bob", "Charlie"} # O(1)查找
if "Bob" in name_set: # 更快
print("找到Bob")
8. 跨平台兼容性
8.1 shebang行设置
确保脚本可移植:
bash复制#!/usr/bin/env bash # 优先使用env查找bash
Python脚本的shebang:
python复制#!/usr/bin/env python3
8.2 路径处理最佳实践
避免硬编码路径:
bash复制script_dir=$(dirname "$0")
config_file="${script_dir}/config.cfg"
Python使用os.path处理路径:
python复制import os
script_dir = os.path.dirname(os.path.abspath(__file__))
config_path = os.path.join(script_dir, "config.cfg")
9. 安全注意事项
9.1 防止脚本注入
Bash中引用变量防止空格问题:
bash复制filename="my file.txt"
rm "$filename" # 正确
rm $filename # 错误! 会尝试删除my和file.txt两个文件
Python防止SQL注入:
python复制# 错误方式
query = f"SELECT * FROM users WHERE name = '{user_input}'"
# 正确方式
cursor.execute("SELECT * FROM users WHERE name = %s", (user_input,))
9.2 权限管理
设置合适的脚本权限:
bash复制chmod 755 script.sh # 所有者可读写执行,其他人可读执行
Python脚本也可以设置可执行权限:
bash复制chmod +x script.py
10. 实际案例解析
10.1 日志分析脚本
处理日志文件的Bash脚本:
bash复制#!/bin/bash
log_file="/var/log/app.log"
output_file="report_$(date +%Y%m%d).txt"
# 统计ERROR出现次数
error_count=$(grep -c "ERROR" "$log_file")
# 提取最后10个错误
last_errors=$(grep "ERROR" "$log_file" | tail -n 10)
{
echo "错误报告 $(date)"
echo "================"
echo "总错误数: $error_count"
echo ""
echo "最近10个错误:"
echo "$last_errors"
} > "$output_file"
10.2 文件备份脚本
Python实现的增量备份:
python复制import os
import shutil
import hashlib
from datetime import datetime
def file_hash(filepath):
"""计算文件哈希值"""
with open(filepath, "rb") as f:
return hashlib.md5(f.read()).hexdigest()
def backup_files(source_dir, backup_dir):
"""增量备份文件"""
if not os.path.exists(backup_dir):
os.makedirs(backup_dir)
backed_up = 0
for root, _, files in os.walk(source_dir):
for filename in files:
src_path = os.path.join(root, filename)
rel_path = os.path.relpath(src_path, source_dir)
dest_path = os.path.join(backup_dir, rel_path)
# 创建目标目录
os.makedirs(os.path.dirname(dest_path), exist_ok=True)
# 检查是否需要备份
if not os.path.exists(dest_path) or \
file_hash(src_path) != file_hash(dest_path):
shutil.copy2(src_path, dest_path)
backed_up += 1
print(f"备份完成,共更新 {backed_up} 个文件")
if __name__ == "__main__":
backup_files("重要文档", "备份/"+datetime.now().strftime("%Y%m%d"))
11. 脚本调试与优化
11.1 性能分析工具
Python的cProfile模块:
python复制import cProfile
def slow_function():
total = 0
for i in range(1000000):
total += i
return total
if __name__ == "__main__":
cProfile.run("slow_function()")
Bash脚本时间测量:
bash复制time ./long_running_script.sh
11.2 代码质量检查
Python的pylint:
bash复制pylint script.py
Bash的shellcheck:
bash复制shellcheck script.sh
12. 脚本打包与分发
12.1 创建可执行包
Python使用PyInstaller:
bash复制pyinstaller --onefile script.py
Bash脚本可以打包依赖:
bash复制# 创建自解压归档
tar czf bundle.tar.gz scripts/ libs/
echo '#!/bin/bash' > installer
echo 'tail -n +3 "$0" | tar xz' >> installer
echo 'exit 0' >> installer
cat bundle.tar.gz >> installer
chmod +x installer
12.2 跨平台分发策略
考虑不同系统的差异:
bash复制#!/bin/bash
case "$(uname -s)" in
Linux*) machine=Linux;;
Darwin*) machine=Mac;;
CYGWIN*) machine=Cygwin;;
MINGW*) machine=MinGw;;
*) machine="UNKNOWN"
esac
echo "检测到系统: $machine"
# 根据系统执行不同命令
if [ "$machine" = "Linux" ]; then
# Linux特有命令
sudo apt update
elif [ "$machine" = "Mac" ]; then
# Mac特有命令
brew update
fi
13. 版本控制与协作
13.1 Git集成技巧
在脚本中添加版本信息:
bash复制#!/bin/bash
VERSION="1.0.2"
LAST_UPDATED="2023-08-15"
show_version() {
echo "脚本版本: $VERSION"
echo "最后更新: $LAST_UPDATED"
git log -n 1 --pretty=format:"Git提交: %h - %s (%ci)"
}
Python脚本获取Git信息:
python复制import subprocess
def get_git_info():
try:
commit_hash = subprocess.check_output(
["git", "rev-parse", "--short", "HEAD"]
).decode().strip()
return f"Git版本: {commit_hash}"
except subprocess.CalledProcessError:
return "未在Git仓库中"
13.2 团队协作规范
统一的脚本头信息:
bash复制#!/bin/bash
#
# 名称: 数据库备份脚本
# 作者: 团队名称
# 创建日期: 2023-01-01
# 修改记录:
# 2023-02-15 - 修复权限问题
# 2023-03-10 - 添加压缩功能
# 描述: 该脚本用于自动备份MySQL数据库到指定目录
# 参数:
# -u 用户名 (必需)
# -d 数据库名 (必需)
# -o 输出目录 (可选,默认为当前目录)
#
Python文档字符串标准:
python复制"""数据库备份工具
该模块提供自动化备份MySQL数据库的功能,支持增量备份和压缩。
典型用法示例:
backup = DatabaseBackup(user='admin', password='secret')
backup.full_backup('mydb', '/backup')
类:
DatabaseBackup: 处理所有备份逻辑的主类
异常:
BackupError: 备份过程中出现的所有错误基类
"""
14. 自动化任务调度
14.1 Cron定时任务
设置Bash脚本定时执行:
bash复制# 编辑当前用户的crontab
crontab -e
# 每天凌晨3点执行备份
0 3 * * * /path/to/backup.sh >> /var/log/backup.log 2>&1
# 每15分钟检查一次服务
*/15 * * * * /path/to/health_check.sh
Python脚本也可以通过cron调度:
bash复制# 每周一早上6点运行Python脚本
0 6 * * 1 /usr/bin/python3 /path/to/weekly_report.py
14.2 系统服务集成
将脚本注册为系统服务(Linux systemd):
ini复制# /etc/systemd/system/script_service.service
[Unit]
Description=我的脚本服务
After=network.target
[Service]
ExecStart=/path/to/script.sh
Restart=on-failure
User=serviceuser
[Install]
WantedBy=multi-user.target
管理服务命令:
bash复制sudo systemctl daemon-reload
sudo systemctl start script_service
sudo systemctl enable script_service # 开机自启
15. 脚本安全加固
15.1 敏感信息处理
不要在脚本中硬编码密码:
bash复制# 从环境变量读取
db_password=${DB_PASSWORD}
# 或者从加密文件读取
password=$(gpg --decrypt password.gpg)
Python使用dotenv管理配置:
python复制from dotenv import load_dotenv
import os
load_dotenv() # 加载.env文件
db_password = os.getenv("DB_PASSWORD")
15.2 权限最小化原则
使用最小必要权限运行脚本:
bash复制#!/bin/bash
# 检查是否以普通用户运行
if [ "$(id -u)" -eq 0 ]; then
echo "错误: 请不要使用root运行此脚本" >&2
exit 1
fi
Python检查运行权限:
python复制import os
import sys
if os.geteuid() == 0:
print("错误: 请不要使用root权限运行此脚本", file=sys.stderr)
sys.exit(1)
16. 脚本测试策略
16.1 单元测试实现
Bash脚本测试框架:
bash复制#!/bin/bash
# test_script.sh
source my_script.sh # 导入要测试的脚本
test_add() {
result=$(add 2 3)
if [ "$result" -eq 5 ]; then
echo "test_add: 通过"
else
echo "test_add: 失败"
exit 1
fi
}
test_add
Python的unittest示例:
python复制import unittest
from my_script import calculate
class TestMyScript(unittest.TestCase):
def test_calculate(self):
self.assertEqual(calculate(3, 4), 7)
self.assertRaises(ValueError, calculate, "a", 1)
if __name__ == "__main__":
unittest.main()
16.2 集成测试方法
使用Docker测试跨平台兼容性:
dockerfile复制FROM ubuntu:20.04
COPY script.sh /app/
WORKDIR /app
RUN chmod +x script.sh
CMD ["./script.sh"]
测试脚本:
bash复制#!/bin/bash
# 构建并运行测试容器
docker build -t script-test .
docker run --rm script-test
17. 脚本文档化
17.1 内联文档标准
Bash脚本注释规范:
bash复制#!/bin/bash
: '
多行注释示例
该脚本用于处理用户数据
支持以下参数:
-f 输入文件
-o 输出目录
'
# 函数注释示例
# 计算两个数字的和
# 参数:
# $1: 第一个数字
# $2: 第二个数字
# 返回:
# 两数之和
add() {
echo $(($1 + $2))
}
Python文档字符串:
python复制def process_data(input_file, output_dir):
"""处理数据文件并生成报告
Args:
input_file (str): 输入文件路径
output_dir (str): 输出目录路径
Returns:
bool: 处理是否成功
Raises:
FileNotFoundError: 当输入文件不存在时
"""
if not os.path.exists(input_file):
raise FileNotFoundError(f"文件不存在: {input_file}")
# 处理逻辑...
return True
17.2 生成外部文档
使用Sphinx为Python脚本生成文档:
bash复制pip install sphinx
sphinx-quickstart docs
cd docs && make html
Bash脚本可以生成man手册:
bash复制.TH MYSCRIPT 1 "2023-08-15" "1.0" "我的脚本手册"
.SH NAME
myscript \- 执行特定任务的脚本
.SH SYNOPSIS
.B myscript
[\-h] [\-v] [\-f
.I FILE
]
.SH DESCRIPTION
这个脚本用来...
18. 脚本性能监控
18.1 资源使用跟踪
监控脚本内存和CPU使用:
bash复制#!/bin/bash
# 记录开始时间
start_time=$(date +%s)
# 主脚本内容...
# 计算运行时间
end_time=$(date +%s)
duration=$((end_time - start_time))
# 获取内存使用峰值
peak_memory=$(ps -o rss= -p $$ | awk '{print $1/1024 " MB"}')
echo "执行时间: $duration 秒"
echo "内存峰值: $peak_memory"
Python使用resource模块:
python复制import resource
import time
start_time = time.time()
start_mem = resource.getrusage(resource.RUSAGE_SELF).ru_maxrss
# 主程序逻辑...
end_mem = resource.getrusage(resource.RUSAGE_SELF).ru_maxrss
print(f"内存使用: {(end_mem - start_mem)/1024:.2f} MB")
print(f"执行时间: {time.time() - start_time:.2f} 秒")
18.2 日志记录策略
Bash脚本日志记录:
bash复制#!/bin/bash
LOG_FILE="script_$(date +%Y%m%d).log"
log() {
local level=$1
local message=$2
echo "$(date '+%Y-%m-%d %H:%M:%S') [$level] $message" | tee -a "$LOG_FILE"
}
log "INFO" "脚本开始执行"
# 业务逻辑...
if [ $? -ne 0 ]; then
log "ERROR" "处理失败"
exit 1
fi
log "INFO" "脚本执行完成"
Python的logging模块:
python复制import logging
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s [%(levelname)s] %(message)s",
handlers=[
logging.FileHandler("app.log"),
logging.StreamHandler()
]
)
logger = logging.getLogger(__name__)
def process_data():
try:
logger.info("开始处理数据")
# 业务逻辑...
logger.info("处理完成")
except Exception as e:
logger.error(f"处理失败: {str(e)}")
raise
19. 脚本维护与演进
19.1 版本升级策略
实现脚本版本检查:
bash复制#!/bin/bash
CURRENT_VERSION="1.2.0"
REMOTE_VERSION=$(curl -s https://example.com/version.txt)
if [ "$CURRENT_VERSION" != "$REMOTE_VERSION" ]; then
echo "发现新版本 $REMOTE_VERSION (当前: $CURRENT_VERSION)"
read -p "是否要更新? [y/N] " choice
if [[ "$choice" =~ ^[Yy]$ ]]; then
curl -s https://example.com/install.sh | bash
fi
fi
Python实现自动更新:
python复制import requests
from packaging import version
def check_update(current_ver):
try:
response = requests.get("https://api.example.com/latest-version")
latest_ver = response.json()["version"]
if version.parse(current_ver) < version.parse(latest_ver):
print(f"发现新版本 {latest_ver} (当前: {current_ver})")
# 下载更新逻辑...
except Exception as e:
print(f"检查更新失败: {e}")
19.2 向后兼容处理
处理参数变更的兼容方式:
bash复制#!/bin/bash
# 旧版本参数: -f file
# 新版本参数: -i input -o output
# 兼容旧用法
if [ "$1" = "-f" ]; then
input_file="$2"
output_file="${2%.*}_out.${2##*.}"
else
# 新参数处理
while getopts "i:o:" opt; do
case $opt in
i) input_file="$OPTARG";;
o) output_file="$OPTARG";;
esac
done
fi
Python使用兼容性包装器:
python复制import warnings
def old_function(param1, param2):
warnings.warn(
"old_function已弃用,请使用new_function",
DeprecationWarning,
stacklevel=2
)
return new_function(param1, param2=param2)
def new_function(param1, *, param2=None, param3=None):
# 新实现...
pass
20. 脚本生态系统集成
20.1 与CI/CD集成
在GitHub Actions中测试脚本:
yaml复制name: Test Script
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Run ShellCheck
uses: ludeeus/action-shellcheck@1.1.0
- name: Test Bash Script
run: |
chmod +x script.sh
./script.sh --test
- name: Test Python Script
run: |
pip install -r requirements.txt
python -m pytest tests/
20.2 与配置管理工具集成
Ansible中使用脚本:
yaml复制- name: 部署脚本
hosts: all
tasks:
- name: 复制脚本文件
copy:
src: files/deploy.sh
dest: /usr/local/bin/deploy
mode: '0755'
- name: 执行部署脚本
command: /usr/local/bin/deploy --env production
register: deploy_result
- name: 检查部署结果
debug:
var: deploy_result.stdout_lines
Python脚本与SaltStack集成:
python复制import salt.client
def apply_config(config_file):
"""通过SaltStack应用配置"""
caller = salt.client.Caller()
return caller.cmd('state.apply', 'script.config', pillar={'config': config_file})
