欢迎光临
我们一直在努力

用Bun ShellScript替代Bash:跨平台脚本自动化的新范式

用Bun ShellScript替代Bash:跨平台脚本自动化的新范式

Bash的痛点:跨平台、错误处理、可读性

独立开发者常写"部署脚本"、"构建脚本"、"数据备份脚本"——传统选择是Bash。但Bash有三个致命问题:

  • 跨平台问题:Bash在macOS/Linux能跑,Windows(即使WSL)可能有兼容性问题
  • 错误处理弱:set -e不是默认,且错误信息不清晰
  • 可读性差:if [ -f "$file" ]; then … 这种语法对新手不友好
  • 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);
    }

    关键改进:

  • 错误处理清晰:try-catch + 清晰的错误信息
  • 健康检查:部署后自动检查,失败则回滚
  • 类型安全:Bun.env.SERVER_IP是string | undefined,编译器会提醒你检查
  • 实战:用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。它的优势:

  • 跨平台:Windows开发,Linux部署——没问题
  • 类型安全:TypeScript帮你避免"环境变量未定义"等低级错误
  • 错误处理:try-catch比set -e清晰得多
  • 可读性:代码即文档,新手也能看懂
  • 迁移策略:

  • 新脚本直接用Bun Shell写
  • 旧脚本在"需要修改"时逐步迁移
  • 用bun run script.ts替代bash script.sh
  • 下一步: 把你的CI/CD流水线、部署脚本、备份脚本都用Bun Shell重写——你会发现"脚本维护"从"最头疼的事"变成"最放心的事"。

    赞(0)
    未经允许不得转载:171主机测评 » 用Bun ShellScript替代Bash:跨平台脚本自动化的新范式
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址