# Script Development

> スクリプト開発ガイド。Shell、Python、Node.jsスクリプト、自動化、バッチ処理、環境変数管理など、効率的なスクリプト開発のベストプラクティス。

- Skill: `majiayu000/script-development` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds add majiayu000/script-development`
- Raw SKILL.md: https://api.skillmd.com/api/skills/majiayu000/script-development/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: majiayu000 (https://skillmd.com/u/majiayu000)
- Updated: 2026-09-09
- Page: https://skillmd.com/skills/majiayu000/script-development

---


# Script Development Skill

## 📋 目次

1. [概要](#概要)
2. [いつ使うか](#いつ使うか)
3. [Shell スクリプト](#shellスクリプト)
4. [Python スクリプト](#pythonスクリプト)
5. [Node.js スクリプト](#nodejsスクリプト)
6. [環境変数管理](#環境変数管理)
7. [実践例](#実践例)
8. [Agent連携](#agent連携)

---

## 概要

このSkillは、スクリプト開発をカバーします：

- **Shell スクリプト** - Bash/Zsh
- **Python スクリプト** - 自動化、データ処理
- **Node.js スクリプト** - TypeScript/JavaScript
- **環境変数管理** - .env、dotenv
- **エラーハンドリング** - 適切なエラー処理
- **ログ出力** - 実行ログ記録

## 📚 公式ドキュメント・参考リソース

**このガイドで学べること**: Shell/Python/Node.jsスクリプトの基本文法、自動化パターン、環境変数管理、エラーハンドリング
**公式で確認すべきこと**: 最新の言語機能、セキュリティベストプラクティス、パッケージのアップデート

### 主要な公式ドキュメント

- **[Bash Reference Manual](https://www.gnu.org/software/bash/manual/)** - Bash公式リファレンス
  - [Shell Builtin Commands](https://www.gnu.org/software/bash/manual/html_node/Shell-Builtin-Commands.html)
  - [Shell Parameters](https://www.gnu.org/software/bash/manual/html_node/Shell-Parameters.html)

- **[Python Documentation](https://docs.python.org/3/)** - Python公式ドキュメント
  - [argparse](https://docs.python.org/3/library/argparse.html)
  - [pathlib](https://docs.python.org/3/library/pathlib.html)

- **[Node.js Documentation](https://nodejs.org/docs/)** - Node.js公式ドキュメント
  - [fs module](https://nodejs.org/api/fs.html)
  - [process module](https://nodejs.org/api/process.html)

- **[Commander.js](https://github.com/tj/commander.js)** - Node.js CLIフレームワーク

### 関連リソース

- **[ShellCheck](https://www.shellcheck.net/)** - Shellスクリプトの静的解析ツール
- **[Python Click](https://click.palletsprojects.com/)** - Pythonコマンドラインインターフェース
- **[Google Shell Style Guide](https://google.github.io/styleguide/shellguide.html)** - Shellスクリプトスタイルガイド

---

## いつ使うか

### 🎯 必須のタイミング

- [ ] 繰り返し作業の自動化時
- [ ] デプロイスクリプト作成時
- [ ] データ移行スクリプト作成時
- [ ] 定期実行バッチ作成時

---

## Shell スクリプト

### 基本構文

```bash
#!/bin/bash

# 変数
NAME="John"
echo "Hello, $NAME"

# コマンドライン引数
echo "First arg: $1"
echo "All args: $@"
echo "Number of args: $#"

# 条件分岐
if [ "$1" = "production" ]; then
  echo "Production mode"
elif [ "$1" = "development" ]; then
  echo "Development mode"
else
  echo "Unknown mode"
fi

# ループ
for file in *.txt; do
  echo "Processing $file"
done

# 関数
function greet() {
  local name=$1
  echo "Hello, $name"
}

greet "Alice"

# エラーハンドリング
set -e  # エラー時に終了
set -u  # 未定義変数使用時にエラー
set -o pipefail  # パイプライン内のエラーを検知

# コマンド実行結果の取得
CURRENT_BRANCH=$(git branch --show-current)
echo "Current branch: $CURRENT_BRANCH"
```

### 実用例：デプロイスクリプト

```bash
#!/bin/bash

# deploy.sh
set -euo pipefail

ENV=${1:-development}

echo "🚀 Deploying to $ENV..."

# ビルド
echo "📦 Building..."
npm run build

# テスト
echo "🧪 Running tests..."
npm test

# デプロイ
if [ "$ENV" = "production" ]; then
  echo "🌍 Deploying to production..."
  ssh user@server 'cd /var/www && git pull && pm2 restart app'
else
  echo "🔧 Deploying to development..."
  ssh user@dev-server 'cd /var/www && git pull && pm2 restart app'
fi

echo "✅ Deploy complete!"
```

---

## Python スクリプト

### 基本構文

```python
#!/usr/bin/env python3

import sys
import os
from pathlib import Path

def main():
    # コマンドライン引数
    if len(sys.argv) < 2:
        print("Usage: script.py <file>")
        sys.exit(1)

    file_path = sys.argv[1]

    # ファイル存在確認
    if not Path(file_path).exists():
        print(f"Error: {file_path} does not exist")
        sys.exit(1)

    # ファイル処理
    with open(file_path, 'r') as f:
        content = f.read()
        print(f"File size: {len(content)} bytes")

if __name__ == "__main__":
    main()
```

### argparse（推奨）

```python
#!/usr/bin/env python3

import argparse
from pathlib import Path

def process_file(file_path: Path, output: Path | None, verbose: bool):
    if verbose:
        print(f"Processing {file_path}...")

    with open(file_path, 'r') as f:
        content = f.read()

    # 処理
    result = content.upper()

    if output:
        with open(output, 'w') as f:
            f.write(result)
        if verbose:
            print(f"Saved to {output}")
    else:
        print(result)

def main():
    parser = argparse.ArgumentParser(description='Process a file')
    parser.add_argument('input', type=Path, help='Input file path')
    parser.add_argument('-o', '--output', type=Path, help='Output file path')
    parser.add_argument('-v', '--verbose', action='store_true', help='Verbose output')

    args = parser.parse_args()

    if not args.input.exists():
        parser.error(f"{args.input} does not exist")

    process_file(args.input, args.output, args.verbose)

if __name__ == "__main__":
    main()

# 使用例:
# python script.py input.txt
# python script.py input.txt -o output.txt -v
```

### データ処理スクリプト

```python
#!/usr/bin/env python3

import pandas as pd
import argparse
from pathlib import Path

def process_csv(input_path: Path, output_path: Path):
    # CSV読み込み
    df = pd.read_csv(input_path)

    # データ処理
    df['total'] = df['price'] * df['quantity']
    df_filtered = df[df['total'] > 1000]

    # 出力
    df_filtered.to_csv(output_path, index=False)
    print(f"✅ Processed {len(df)} rows → {len(df_filtered)} rows")

def main():
    parser = argparse.ArgumentParser()
    parser.add_argument('input', type=Path)
    parser.add_argument('output', type=Path)
    args = parser.parse_args()

    process_csv(args.input, args.output)

if __name__ == "__main__":
    main()
```

---

## Node.js スクリプト

### 基本構文（TypeScript）

```typescript
#!/usr/bin/env ts-node

import { readFileSync, writeFileSync } from 'fs'
import { program } from 'commander'

program
  .name('process-file')
  .description('Process a file')
  .argument('<input>', 'Input file path')
  .option('-o, --output <path>', 'Output file path')
  .option('-v, --verbose', 'Verbose output')
  .action((input, options) => {
    if (options.verbose) {
      console.log(`Processing ${input}...`)
    }

    const content = readFileSync(input, 'utf-8')
    const result = content.toUpperCase()

    if (options.output) {
      writeFileSync(options.output, result)
      if (options.verbose) {
        console.log(`Saved to ${options.output}`)
      }
    } else {
      console.log(result)
    }
  })

program.parse()

// package.json
{
  "scripts": {
    "process": "ts-node src/process.ts"
  },
  "bin": {
    "process-file": "./dist/process.js"
  }
}
```

### ファイル操作

```typescript
import fs from 'fs/promises'
import path from 'path'

async function processFiles(dirPath: string) {
  try {
    // ディレクトリ内のファイル一覧取得
    const files = await fs.readdir(dirPath)

    for (const file of files) {
      const filePath = path.join(dirPath, file)
      const stat = await fs.stat(filePath)

      if (stat.isFile() && file.endsWith('.txt')) {
        // ファイル処理
        const content = await fs.readFile(filePath, 'utf-8')
        const processed = content.toUpperCase()

        await fs.writeFile(filePath, processed)
        console.log(`Processed: ${file}`)
      }
    }
  } catch (error) {
    console.error('Error:', error)
    process.exit(1)
  }
}

processFiles('./data')
```

### API呼び出し

```typescript
import axios from 'axios'

async function fetchUsers() {
  try {
    const response = await axios.get('https://api.example.com/users', {
      headers: {
        'Authorization': `Bearer ${process.env.API_TOKEN}`
      }
    })

    console.log(`Fetched ${response.data.length} users`)

    // CSVに出力
    const csv = response.data
      .map((user: any) => `${user.id},${user.name},${user.email}`)
      .join('\n')

    await fs.writeFile('users.csv', `id,name,email\n${csv}`)
    console.log('✅ Saved to users.csv')
  } catch (error) {
    console.error('Error:', error)
    process.exit(1)
  }
}

fetchUsers()
```

---

## 環境変数管理

### .env ファイル

```bash
# .env
DATABASE_URL=postgresql://localhost:5432/mydb
API_KEY=your_api_key_here
NODE_ENV=development
```

### Node.js（dotenv）

```typescript
import 'dotenv/config'

console.log(process.env.DATABASE_URL)
console.log(process.env.API_KEY)

// 必須チェック
const requiredEnvVars = ['DATABASE_URL', 'API_KEY']

for (const envVar of requiredEnvVars) {
  if (!process.env[envVar]) {
    console.error(`Error: ${envVar} is not set`)
    process.exit(1)
  }
}
```

### Python（python-dotenv）

```python
from dotenv import load_dotenv
import os

load_dotenv()

DATABASE_URL = os.getenv('DATABASE_URL')
API_KEY = os.getenv('API_KEY')

# 必須チェック
required_env_vars = ['DATABASE_URL', 'API_KEY']

for env_var in required_env_vars:
    if not os.getenv(env_var):
        print(f"Error: {env_var} is not set")
        exit(1)
```

### Shell

```bash
#!/bin/bash

# .envファイルを読み込み
if [ -f .env ]; then
  export $(cat .env | grep -v '^#' | xargs)
fi

echo "DATABASE_URL: $DATABASE_URL"
```

---

## 実践例

### Example 1: データ移行スクリプト（Python）

```python
#!/usr/bin/env python3

import psycopg2
from dotenv import load_dotenv
import os
import sys

load_dotenv()

def migrate_data():
    # 接続
    conn = psycopg2.connect(os.getenv('DATABASE_URL'))
    cur = conn.cursor()

    try:
        # データ移行
        cur.execute("""
            INSERT INTO new_users (id, name, email)
            SELECT id, name, email FROM old_users
            WHERE migrated = false
        """)

        # フラグ更新
        cur.execute("UPDATE old_users SET migrated = true")

        conn.commit()
        print(f"✅ Migrated {cur.rowcount} rows")

    except Exception as e:
        conn.rollback()
        print(f"❌ Error: {e}")
        sys.exit(1)

    finally:
        cur.close()
        conn.close()

if __name__ == "__main__":
    migrate_data()
```

### Example 2: 定期バックアップスクリプト（Shell）

```bash
#!/bin/bash

# backup.sh
set -euo pipefail

BACKUP_DIR="/var/backups/db"
DATE=$(date +%Y%m%d_%H%M%S)
BACKUP_FILE="$BACKUP_DIR/backup_$DATE.sql.gz"

# ディレクトリ作成
mkdir -p "$BACKUP_DIR"

# バックアップ実行
echo "📦 Creating backup..."
pg_dump -U postgres mydb | gzip > "$BACKUP_FILE"

echo "✅ Backup saved: $BACKUP_FILE"

# 古いバックアップ削除（7日以上前）
find "$BACKUP_DIR" -name "backup_*.sql.gz" -mtime +7 -delete
echo "🗑️  Old backups deleted"

# crontabで毎日実行
# 0 2 * * * /path/to/backup.sh >> /var/log/backup.log 2>&1
```

---

## Agent連携

### 📖 Agentへの指示例

**Shell スクリプト作成**
```
以下を行うShell スクリプトを作成してください：
1. GitHubから最新コードをpull
2. npmビルド実行
3. PM2でアプリ再起動
4. エラー時は通知
```

**Python データ処理スクリプト作成**
```
CSV ファイルを読み込み、以下の処理を行うPython スクリプトを作成してください：
- priceとquantityからtotalを計算
- total > 1000の行のみフィルタリング
- 結果を新しいCSV ファイルに出力
```

---

## まとめ

### スクリプト開発のベストプラクティス

1. **エラーハンドリング** - set -e（Shell）、try-catch（Python/Node.js）
2. **環境変数** - .envファイルで管理
3. **引数パース** - argparse（Python）、commander（Node.js）
4. **ログ出力** - 実行状況を記録

---

_Last updated: 2025-12-24_

