用Bun ShellScript替代Bash:跨平台脚本自动化的新范式
Bash的痛点:跨平台、错误处理、可读性
独立开发者常写"部署脚本"、"构建脚本"、"数据备份脚本"——传统选择是Bash。但Bash有三个致命问题:
Bun Shell的解决方案:
Bun提供了Bun.$API——用TypeScript写Shell脚本,且跨平台兼容。
Bun Shell核心API:比Bash简洁10倍
执行命令:
// 传统Bash
// if ! command -v docker &> /dev/null; then
// echo "Docker not installed"
// exit 1
// fi
// Bun Shell
import { $ } from 'bun';
// 执行命令(自动检查失败)
try {
await $`docker ps`;
} catch (error) {
console.error('Docker not running:', error.message);
process.exit(1);
}
管道(Pipe):
// 传统Bash:cat access.log | grep "404" | wc -l
// Bun Shell
const result = await $`cat access.log | grep "404" | wc -l`.text();
console.log('404 errors:', result.trim());
重定向:
// 传统Bash:echo "Hello" > output.txt 2> error.log
// Bun Shell
await $`echo "Hello"`.redirect('stdout', 'output.txt');
await $`some-command`.redirect('stderr', 'error.log');
// 更简洁的语法(Bun v1.0.18+)
await Bun.write('output.txt', $`echo "Hello"`);
环境变量:
// 设置环境变量(仅对当前命令有效)
await $`DATABASE_URL=postgres://localhost/myapp npm run db:migrate`;
// 读取环境变量
console.log(Bun.env.DATABASE_URL);
实战:用Bun Shell写"部署脚本"
场景: 部署Next.js应用到VPS(用Docker)
传统Bash版本(deploy.sh):
#!/bin/bash
set -e # 任何命令失败就退出
# 1. 检查环境变量
if [ -z "$SERVER_IP" ]; then
echo "ERROR: SERVER_IP not set"
exit 1
fi
# 2. 构建Docker镜像
echo "Building Docker image…"
docker build -t myapp:latest .
if [ $? -ne 0 ]; then
echo "ERROR: Docker build failed"
exit 1
fi
# 3. 推送镜像到Docker Hub
echo "Pushing to Docker Hub…"
docker push myusername/myapp:latest
if [ $? -ne 0 ]; then
echo "ERROR: Docker push failed"
exit 1
fi
# 4. SSH到服务器,拉取并重启容器
echo "Deploying to server…"
ssh user@$SERVER_IP << 'ENDSSH'
docker pull myusername/myapp:latest
docker stop myapp || true
docker rm myapp || true
docker run -d –name myapp -p 3000:3000 myusername/myapp:latest
ENDSSH
echo "Deploy successful!"
Bun Shell版本(deploy.ts):
import { $, sleep } from 'bun';
// 1. 检查环境变量(类型安全)
if (!Bun.env.SERVER_IP) {
console.error('ERROR: SERVER_IP not set');
process.exit(1);
}
console.log('Starting deployment…');
try {
// 2. 构建Docker镜像
console.log('Building Docker image…');
await $`docker build -t myapp:latest .`;
// 3. 推送镜像
console.log('Pushing to Docker Hub…');
await $`docker push myusername/myapp:latest`;
// 4. SSH部署
console.log('Deploying to server…');
await $`ssh user@${Bun.env.SERVER_IP} << 'ENDSSH'
docker pull myusername/myapp:latest
docker stop myapp || true
docker rm myapp || true
docker run -d –name myapp -p 3000:3000 –restart unless-stopped myusername/myapp:latest
ENDSSH`;
// 5. 健康检查
console.log('Waiting for health check…');
await sleep(5);
const health = await $`curl -s -o /dev/null -w "%{http_code}" http://${Bun.env.SERVER_IP}:3000/api/health`;
if (health.stdout.trim() !== '200') {
throw new Error(`Health check failed: ${health.stdout}`);
}
console.log('✅ Deploy successful!');
} catch (error) {
console.error('❌ Deploy failed:', error.message);
// 自动回滚(如果上一版本存在)
console.log('Attempting rollback…');
await $`ssh user@${Bun.env.SERVER_IP} docker run -d –name myapp -p 3000:3000 myusername/myapp:previous || true`;
process.exit(1);
}
关键改进:
实战:用Bun Shell写"数据库备份脚本"
场景: 每天凌晨2点自动备份PostgreSQL数据库到S3
Bun Shell版本(backup.ts):
import { $, cron } from 'bun';
import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3';
const s3 = new S3Client({ region: 'us-east-1' });
async function backupDatabase() {
const timestamp = new Date().toISOString().split('T')[0]; // YYYY-MM-DD
const backupFile = `backup-${timestamp}.sql`;
try {
console.log(`Starting backup: ${backupFile}`);
// 1. 导出数据库
await $`pg_dump $DATABASE_URL > ${backupFile}`;
// 2. 压缩
await $`gzip ${backupFile}`;
const compressedFile = `${backupFile}.gz`;
// 3. 上传到S3
const fileContent = await Bun.file(compressedFile).arrayBuffer();
await s3.send(new PutObjectCommand({
Bucket: 'myapp-backups',
Key: `daily/${compressedFile}`,
Body: Buffer.from(fileContent),
}));
console.log(`✅ Backup uploaded: s3://myapp-backups/daily/${compressedFile}`);
// 4. 清理本地文件(保留最近7天)
await $`find . -name "backup-*.sql.gz" -mtime +7 -delete`;
// 5. 清理S3旧备份(保留30天)
// (用AWS CLI或S3 API删除超过30天的对象)
await $`aws s3 ls s3://myapp-backups/daily/ | awk '{if (NR > 30) print $4}' | xargs -I {} aws s3 rm s3://myapp-backups/daily/{}`;
} catch (error) {
console.error('❌ Backup failed:', error.message);
// 发送告警(用Slack Webhook)
await fetch(Bun.env.SLACK_WEBHOOK_URL!, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
text: `🚨 Database backup failed: ${error.message}`,
}),
});
process.exit(1);
}
}
// 如果直接运行,立即执行
if (import.meta.main) {
await backupDatabase();
}
// 如果作为模块导入,导出给cron使用
export { backupDatabase };
设置定时任务(用Bun的cron包):
// cron.ts
import { Cron } from 'bun';
import { backupDatabase } from './backup';
// 每天凌晨2点执行
new Cron('0 2 * * *', async () => {
console.log('Running scheduled backup…');
await backupDatabase();
});
console.log('Cron job started: Daily backup at 2:00 AM');
运行cron:
# 用Bun运行(会自动保持进程)
bun run cron.ts
# 或用PM2保持进程
pm2 start bun –name "backup-cron" — run cron.ts
跨平台兼容性:Windows/macOS/Linux
Bun Shell的命令会自动转换成"当前平台的可执行格式"。
示例:跨平台的文件操作
// 这段脚本在Windows/macOS/Linux都能跑
import { $ } from 'bun';
import { mkdir, write } from 'bun';
// 1. 创建目录(跨平台)
await mkdir('dist', { recursive: true });
// 2. 写入文件
await write('dist/version.txt', '1.0.0');
// 3. 执行平台特定的命令
if (process.platform === 'win32') {
await $`dir dist`;
} else {
await $`ls -la dist`;
}
在CI/CD里使用Bun Shell:
# .github/workflows/deploy.yml
name: Deploy
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
– uses: actions/checkout@v3
– name: Install Bun
run: |
curl -fsSL https://bun.sh/install | bash
echo "$HOME/.bun/bin" >> $GITHUB_PATH
– name: Run deploy script
run: bun run scripts/deploy.ts
env:
SERVER_IP: ${{ secrets.SERVER_IP }}
DATABASE_URL: ${{ secrets.DATABASE_URL }}
调试技巧:让脚本错误一目了然
Bun Shell的错误信息比Bash清晰得多:
// 当命令失败时,Bun会抛出清晰的错误
try {
await $`docker push myusername/myapp:latest`;
} catch (error) {
console.error('Error details:');
console.error('- Command:', error.cmd); // 完整的命令
console.error('- Exit code:', error.exitCode);
console.error('- Stdout:', error.stdout);
console.error('- Stderr:', error.stderr);
}
用–verbose模式调试:
// 在脚本开头设置
$.verbose = true; // 会打印每个执行的命令
await $`docker build -t myapp .`; // 会打印:> docker build -t myapp .
结论:Bun Shell是"DevOps脚本"的未来
如果你现在还在写Bash脚本,应该考虑迁移到Bun Shell。它的优势:
迁移策略:
下一步: 把你的CI/CD流水线、部署脚本、备份脚本都用Bun Shell重写——你会发现"脚本维护"从"最头疼的事"变成"最放心的事"。


