| name | script-development |
| description | スクリプト開発ガイド。Shell、Python、Node.jsスクリプト、自動化、バッチ処理、環境変数管理など、効率的なスクリプト開発のベストプラクティス。 |
Script Development Skill
📋 目次
概要
このSkillは、スクリプト開発をカバーします:
- Shell スクリプト - Bash/Zsh
- Python スクリプト - 自動化、データ処理
- Node.js スクリプト - TypeScript/JavaScript
- 環境変数管理 - .env、dotenv
- エラーハンドリング - 適切なエラー処理
- ログ出力 - 実行ログ記録
いつ使うか
🎯 必須のタイミング
- 繰り返し作業の自動化時
- デプロイスクリプト作成時
- データ移行スクリプト作成時
- 定期実行バッチ作成時
Shell スクリプト
基本構文
#!/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"
実用例:デプロイスクリプト
#!/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 スクリプト
基本構文
#!/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(推奨)
#!/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
データ処理スクリプト
#!/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)
#!/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"
}
}
ファイル操作
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呼び出し
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 ファイル
# .env
DATABASE_URL=postgresql://localhost:5432/mydb
API_KEY=your_api_key_here
NODE_ENV=development
Node.js(dotenv)
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)
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
#!/bin/bash
# .envファイルを読み込み
if [ -f .env ]; then
export $(cat .env | grep -v '^#' | xargs)
fi
echo "DATABASE_URL: $DATABASE_URL"
実践例
Example 1: データ移行スクリプト(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)
#!/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 ファイルに出力
まとめ
スクリプト開発のベストプラクティス
- エラーハンドリング - set -e(Shell)、try-catch(Python/Node.js)
- 環境変数 - .envファイルで管理
- 引数パース - argparse(Python)、commander(Node.js)
- ログ出力 - 実行状況を記録
Last updated: 2025-12-24