1. 脚本编程基础概述
在数字化办公和自动化处理成为标配的今天,掌握常用脚本语句就像拥有了一把瑞士军刀。无论是系统管理员批量处理日志,还是数据分析师清洗Excel表格,抑或是前端开发者自动化构建流程,脚本都能将重复劳动转化为一键执行。不同于完整编程语言,脚本语句的特点是短小精悍、即写即用,特别适合解决那些"不值得写完整程序但又必须自动化"的日常任务。
我至今记得第一次用5行bash脚本完成服务器日志归档时的震撼——原本需要手动操作半小时的工作,现在只需双击脚本文件。这种效率提升正是脚本语言的魅力所在。本文将重点覆盖Bash、Python和PowerShell三大平台的经典语句,这些语句经过我十年运维开发生涯的反复验证,每个都能解决实际工作中的痛点问题。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 核心脚本语句分类解析
2.1 文件操作语句
文件处理是脚本最常用的场景之一。在Linux环境下,find命令配合xargs堪称黄金组合:
bash复制# 查找并压缩7天前的日志文件
find /var/log -name "*.log" -mtime +7 -print0 | xargs -0 gzip
这里有几个关键点:
-print0和-0参数处理含空格的文件名-mtime +7表示修改时间超过7天- 管道符
|将find结果传递给xargs
Windows PowerShell中同等功能的实现更直观:
powershell复制# 获取并压缩30天前的日志
Get-ChildItem -Path "C:\Logs\*.log" | Where-Object {$_.LastWriteTime -lt (Get-Date).AddDays(-30)} | Compress-Archive -DestinationPath "C:\Archives\logs_$(Get-Date -Format 'yyyyMMdd').zip"
重要提示:生产环境执行删除操作前务必先打印确认文件列表,可以先用
-ls替代-delete进行预演
2.2 文本处理语句
文本处理三剑客grep、awk、sed各有专长。提取Nginx访问日志中状态码为500的请求:
bash复制awk '$9 == 500 {print $7}' access.log | sort | uniq -c | sort -nr
这个语句链的妙处在于:
- awk按列过滤(第9列是状态码)
- 第一轮sort为uniq准备输入
- uniq -c统计出现次数
- 最后按频次倒排
Python的pandas库在复杂文本处理时更得心应手:
python复制import pandas as pd
df = pd.read_csv('data.csv', delimiter='|')
df.query('price > 100').groupby('category')['sales'].sum().to_csv('result.csv')
2.3 系统管理语句
进程管理是运维的日常。检测并重启挂死的服务:
bash复制if ! pgrep -x "nginx" > /dev/null; then
systemctl start nginx
echo "$(date) - Nginx restarted" >> /var/log/watchdog.log
fi
Windows下的等效PowerShell脚本:
powershell复制if (-not (Get-Process -Name "tomcat" -ErrorAction SilentlyContinue)) {
Start-Service -Name "Tomcat9"
Add-Content -Path "C:\logs\watchdog.log" -Value "$(Get-Date) - Tomcat restarted"
}
3. 跨平台脚本实战技巧
3.1 条件判断最佳实践
条件语句的陷阱往往在边界条件。检查文件存在的正确方式:
bash复制# 错误示范:可能误判符号链接
if [ -f "/path/to/file" ]; then...
# 正确做法:明确检测类型
if [[ -e "/path/to/file" && ! -L "/path/to/file" ]]; then...
Python中处理路径更推荐使用pathlib:
python复制from pathlib import Path
config_file = Path('/etc/app.conf')
if config_file.exists() and not config_file.is_symlink():
...
3.2 循环优化技巧
处理大量文件时,for循环的效率至关重要。对比三种遍历方式:
bash复制# 方式1:传统for(性能最差)
for file in $(ls *.log); do...
# 方式2:glob扩展(中等)
for file in *.log; do...
# 方式3:find + exec(最佳)
find . -name "*.log" -exec process_file.sh {} \;
Python中使用生成器表达式处理大文件:
python复制with open('huge.log') as f:
error_lines = (line for line in f if 'ERROR' in line)
for err in error_lines:
...
4. 调试与错误处理
4.1 调试语句
Bash脚本的调试开关:
bash复制#!/bin/bash -x # 开启调试模式
set -euo pipefail # 严格模式:错误退出、未定义变量检测、管道错误检测
Python的调试利器:
python复制import pdb
def problematic_func():
pdb.set_trace() # 交互式调试
...
4.2 错误日志规范
生产环境脚本必须记录结构化日志:
bash复制log() {
local level=$1
local message=$2
echo "$(date '+%Y-%m-%d %H:%M:%S') [${level}] ${message}" >> /var/log/script.log
[ "$level" = "ERROR" ] && alert_ops "$message"
}
log "INFO" "Processing started"
Python的logging模块更强大:
python复制import logging
logging.basicConfig(
filename='app.log',
format='%(asctime)s [%(levelname)s] %(message)s',
level=logging.INFO
)
5. 性能优化关键点
5.1 减少子进程调用
Bash中频繁调用外部命令会显著降低性能。对比两种实现:
bash复制# 低效方式:每次循环都调用date
for i in {1..100}; do
echo "$(date +%s) - $i"
done
# 高效方式:单次获取时间戳
now=$(date +%s)
for i in {1..100}; do
echo "$now - $i"
done
5.2 并行处理技巧
GNU parallel工具大幅提升批量任务速度:
bash复制# 串行处理
for f in *.csv; do
process_file "$f"
done
# 并行处理(4个并发)
find . -name "*.csv" | parallel -j4 process_file
Python的concurrent.futures模块:
python复制from concurrent.futures import ThreadPoolExecutor
with ThreadPoolExecutor(max_workers=4) as executor:
executor.map(process_file, glob.glob('*.csv'))
6. 安全编码规范
6.1 输入验证
永远不要相信外部输入:
bash复制# 危险操作:直接使用参数
rm -rf "$1"
# 安全做法:验证路径
target_dir=$(realpath "$1")
[[ "$target_dir" == /safe/path/* ]] || exit 1
rm -rf "$target_dir"
Python的输入消毒:
python复制import re
user_input = input("Enter path: ")
if not re.match(r'^/safe/path/[a-z0-9_]+$', user_input):
raise ValueError("Invalid path")
6.2 权限控制
最小权限原则的实施:
bash复制# 错误示范:以root运行整个脚本
#!/bin/bash
process_data() {
# 实际不需要root权限的操作
...
}
# 正确做法:仅特权部分使用sudo
sudo needed_command
regular_command
7. 版本控制与复用
7.1 函数库管理
将常用函数组织成可复用的库:
bash复制# lib/utils.sh
log_info() {
echo "$(date) [INFO] $*"
}
# 主脚本
source "$(dirname "$0")/lib/utils.sh"
log_info "Starting process"
Python的模块化更完善:
python复制# utils/logger.py
def setup_logger(name):
import logging
logger = logging.getLogger(name)
...
# main.py
from utils.logger import setup_logger
7.2 参数化设计
使脚本可配置化的几种方式:
- 环境变量方式:
bash复制#!/bin/bash
: ${MAX_RETRY:=3} # 默认值
for ((i=1; i<=MAX_RETRY; i++)); do
...
done
- 配置文件方式(config.ini):
python复制import configparser
config = configparser.ConfigParser()
config.read('config.ini')
timeout = config.getint('DEFAULT', 'Timeout')
8. 实用代码片段集锦
8.1 日期处理
获取昨天日期的跨平台方案:
bash复制# Linux
yesterday=$(date -d "yesterday" +%Y-%m-%d)
# MacOS/BSD
yesterday=$(date -v-1d +%Y-%m-%d)
# PowerShell
$yesterday = (Get-Date).AddDays(-1).ToString("yyyy-MM-dd")
8.2 网络检测
检查服务可用性的进阶方法:
bash复制wait_for_port() {
local host=$1 port=$2 timeout=${3:-30}
for ((i=0; i<timeout; i++)); do
if nc -z -w1 "$host" "$port"; then
return 0
fi
sleep 1
done
return 1
}
Python requests版:
python复制import requests
from urllib3.util.retry import Retry
session = requests.Session()
retries = Retry(total=3, backoff_factor=1)
session.mount('http://', requests.adapters.HTTPAdapter(max_retries=retries))
9. 脚本工程化建议
9.1 目录结构规范
中型脚本项目的推荐布局:
code复制project/
├── bin/ # 可执行脚本
├── lib/ # 函数库
├── etc/ # 配置文件
├── tests/ # 测试用例
├── logs/ # 日志文件
└── README.md # 使用说明
9.2 单元测试实施
Bash脚本测试框架推荐bats-core:
bash复制# test/example.bats
@test "测试文件存在检测" {
run check_file "/etc/passwd"
[ "$status" -eq 0 ]
}
Python的标准unittest:
python复制import unittest
class TestScript(unittest.TestCase):
def test_file_check(self):
self.assertTrue(check_file('/etc/passwd'))
10. 性能监控与调优
10.1 执行时间测量
精确到毫秒的耗时统计:
bash复制start=$(date +%s.%N)
# 执行代码...
end=$(date +%s.%N)
runtime=$(echo "$end - $start" | bc)
echo "耗时: ${runtime}秒"
Python的timeit模块更精准:
python复制import timeit
time = timeit.timeit('"-".join(str(n) for n in range(100))', number=10000)
10.2 资源监控
跟踪脚本内存使用:
bash复制# 使用GNU time命令(不是shell内建的)
/usr/bin/time -f "内存峰值: %M KB" ./script.sh
Python的memory_profiler:
python复制@profile
def memory_intensive_func():
...
在实际工作中,我发现很多脚本问题都源于对边界条件的忽视。比如最近遇到一个案例:备份脚本在跨月时失效,因为作者假设日期格式始终是两位数(03 vs 3)。这提醒我们:好的脚本不仅要处理常规情况,更要预见各种异常场景。建议每个关键脚本都至少考虑以下测试用例:空输入、超大输入、特殊字符、权限不足、磁盘已满、网络中断等情况下的行为。
