cli-development

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

CLI Development Guidelines

CLI开发指南

Best practices for designing and implementing command-line interface (CLI) applications across programming languages.
跨编程语言设计与实现命令行界面(CLI)应用程序的最佳实践。

CLI Design Principles

CLI设计原则

Unix Philosophy

Unix哲学

Follow the core Unix philosophy for robust, composable CLIs:
  • Do one thing well - Each command should have a single, well-defined responsibility
  • Make it composable - Design output to work as input to other programs
  • Handle text streams - CLIs work with stdin/stdout/stderr
  • Exit cleanly - Use appropriate exit codes (0 for success, non-zero for errors)
  • Fail fast - Detect and report problems immediately
  • Be scriptable - All functionality must be accessible from non-interactive contexts
遵循核心Unix哲学,构建健壮、可组合的CLI:
  • 专注做好一件事 - 每个命令应具备单一、明确的职责
  • 支持组合使用 - 设计输出格式以适配其他程序的输入
  • 处理文本流 - CLI基于stdin/stdout/stderr工作
  • 干净退出 - 使用合适的退出码(0表示成功,非零表示错误)
  • 快速失败 - 立即检测并报告问题
  • 支持脚本化 - 所有功能必须可通过非交互式上下文访问

Command Structure

命令结构

Verb-Noun Pattern

动词-名词模式

Organize commands using verb-noun relationships:
undefined
使用动词-名词关系组织命令:
undefined

Good: clear action and object

推荐:动作和对象清晰明确

git add <file> # verb: add, noun: file docker run <image> # verb: run, noun: image npm install <package> # verb: install, noun: package
git add <file> # 动词: add,名词: file docker run <image> # 动词: run,名词: image npm install <package> # 动词: install,名词: package

Avoid: unclear structure

避免:结构模糊

git foo # ambiguous docker something # unclear what happens
undefined
git foo # 含义不明确 docker something # 无法清晰了解操作内容
undefined

Subcommands vs Flags

子命令与标志对比

Use subcommands when:
  • Commands have distinct behaviors or workflows
  • Different sets of options apply to different operations
  • You want clear logical grouping
git branch                    # subcommand
git branch --list             # subcommand with flag
git branch --delete <name>    # subcommand with flag and argument
Use flags when:
  • Modifying behavior of a single operation
  • Toggling optional features
  • Providing configuration
ls --color          # flag modifies ls behavior
grep -r --include   # flags modify search behavior
Never mix deeply:
undefined
使用子命令的场景:
  • 命令具有截然不同的行为或工作流
  • 不同操作适用不同的选项集合
  • 需要清晰的逻辑分组
git branch                    # 子命令
git branch --list             # 带标志的子命令
git branch --delete <name>    # 带标志和参数的子命令
使用标志的场景:
  • 修改单一操作的行为
  • 切换可选功能
  • 提供配置项
ls --color          # 标志修改ls的行为
grep -r --include   # 标志修改搜索行为
切勿深度混合:
undefined

Avoid: unclear hierarchy

避免:层级模糊

tool --verbose subcommand --debug --mode strict
tool --verbose subcommand --debug --mode strict

Better: clear structure

推荐:结构清晰

tool subcommand --verbose --debug --mode strict
undefined
tool subcommand --verbose --debug --mode strict
undefined

Command Hierarchy

命令层级

Keep hierarchy shallow (max 2-3 levels):
undefined
保持层级简洁(最多2-3级):
undefined

Good: clear, discoverable

推荐:清晰、易发现

aws s3 ls aws ec2 describe-instances
aws s3 ls aws ec2 describe-instances

Avoid: too deep

避免:层级过深

cloud provider storage list all buckets
undefined
cloud provider storage list all buckets
undefined

Argument Parsing

参数解析

Argument Types

参数类型

Positional Arguments
  • Required by default
  • Appear in specific positions
  • Use for primary input/object
bash
cp <source> <destination>   # Two required positional arguments
docker run <image>          # One required positional argument
Optional Arguments (with defaults)
  • Used less frequently
  • Should have sensible defaults
  • Clearly documented
bash
npm install [directory]     # Optional, defaults to current directory
grep [options] <pattern> [file]  # File argument optional
位置参数
  • 默认必填
  • 出现在特定位置
  • 用于主要输入/对象
bash
cp <source> <destination>   # 两个必填位置参数
docker run <image>          # 一个必填位置参数
可选参数(带默认值)
  • 使用频率较低
  • 应具备合理的默认值
  • 需清晰文档说明
bash
npm install [directory]     # 可选参数,默认值为当前目录
grep [options] <pattern> [file]  # 文件参数为可选

Flags and Options

标志与选项

Short Flags (-f)
  • Single letter, used frequently
  • For common operations
bash
ls -l          # long format
grep -r        # recursive
tar -xvf       # combined flags
Long Flags (--flag)
  • Verbose, self-documenting
  • For less-common operations
  • Preferred in scripts
bash
docker run --detach --name myapp
npm install --save-dev
grep --recursive --include="*.js"
Flag Styles
undefined
短标志 (-f)
  • 单个字母,高频使用
  • 适用于常见操作
bash
ls -l          # 长格式
grep -r        # 递归搜索
tar -xvf       # 组合标志
长标志 (--flag)
  • 描述性强,自文档化
  • 适用于低频操作
  • 脚本中优先使用
bash
docker run --detach --name myapp
npm install --save-dev
grep --recursive --include="*.js"
标志格式
undefined

Boolean flags

布尔标志

--verbose # boolean true/false --color=always # explicit value
--verbose # 布尔值true/false --color=always # 显式指定值

Flags with values

带值的标志

--output file.txt # space-separated --output=file.txt # equals-separated
--output file.txt # 空格分隔 --output=file.txt # 等号分隔

Accept both styles when possible

尽可能同时支持两种格式

--timeout 30 or --timeout=30
undefined
--timeout 30 或 --timeout=30
undefined

Environment Variables

环境变量

Convention: Use SCREAMING_SNAKE_CASE for environment variables
bash
export DEBUG=1
export LOG_LEVEL=debug
export API_TOKEN=secret
Hierarchy (high to low precedence):
  1. Command-line flags (highest priority - most explicit)
  2. Environment variables (middle - applies to multiple invocations)
  3. Configuration file (lower - shared settings)
  4. Built-in defaults (lowest priority - fallback)
Example precedence:
bash
undefined
约定: 使用SCREAMING_SNAKE_CASE格式命名环境变量
bash
export DEBUG=1
export LOG_LEVEL=debug
export API_TOKEN=secret
优先级(从高到低):
  1. 命令行标志(最高优先级 - 最显式)
  2. 环境变量(中间优先级 - 适用于多次调用)
  3. 配置文件(较低优先级 - 共享设置)
  4. 内置默认值(最低优先级 - 回退方案)
优先级示例:
bash
undefined

If user runs with flag, it takes precedence

如果用户使用标志调用,标志优先级最高

tool --timeout 10 # Uses 10, ignores TIMEOUT env var
tool --timeout 10 # 使用10,忽略TIMEOUT环境变量

If no flag, check env var

如果没有标志,检查环境变量

export TIMEOUT=5 tool # Uses 5 from TIMEOUT
export TIMEOUT=5 tool # 使用TIMEOUT环境变量的值5

If no flag or env var, use config file default

如果没有标志或环境变量,使用配置文件中的默认值

tool --config config.yaml # Reads timeout from config.yaml
tool --config config.yaml # 从config.yaml读取timeout值

If nothing specified, use built-in default

如果未指定任何内容,使用内置默认值

tool # Uses hardcoded default (e.g., 30)
undefined
tool # 使用硬编码的默认值(例如30)
undefined

Configuration Files

配置文件

Location convention:
  • Linux/macOS:
    ~/.config/app/config.yaml
    or
    ~/.apprc
  • Windows:
    %APPDATA%\App\config.yaml
    or
    ~\AppData\Local\App\config.yaml
  • All platforms:
    ./config.yaml
    (current directory, highest priority)
Format preference: YAML > TOML > JSON > INI
  • YAML is human-readable and concise
  • TOML is simpler than YAML but supports nested structures
  • JSON is universal but verbose
  • INI is legacy
Example config file:
yaml
undefined
位置约定:
  • Linux/macOS:
    ~/.config/app/config.yaml
    ~/.apprc
  • Windows:
    %APPDATA%\App\config.yaml
    ~\AppData\Local\App\config.yaml
  • 全平台:
    ./config.yaml
    (当前目录,优先级最高)
格式偏好: YAML > TOML > JSON > INI
  • YAML可读性强且简洁
  • TOML比YAML更简单,但支持嵌套结构
  • JSON通用性强但冗长
  • INI为遗留格式
配置文件示例:
yaml
undefined

~/.config/myapp/config.yaml

~/.config/myapp/config.yaml

debug: false log_level: info timeout: 30 output_format: json color: true
api: endpoint: https://api.example.com timeout: 10

**Load with proper precedence:**
```python
debug: false log_level: info timeout: 30 output_format: json color: true
api: endpoint: https://api.example.com timeout: 10

**按优先级加载:**
```python

Python example

Python示例

import os from pathlib import Path import yaml
def load_config(): # Built-in defaults config = { 'debug': False, 'timeout': 30, 'color': True }
# Load from config file
config_path = Path.home() / '.config' / 'myapp' / 'config.yaml'
if config_path.exists():
    with open(config_path) as f:
        file_config = yaml.safe_load(f)
        config.update(file_config)

# Override with environment variables
if os.getenv('DEBUG'):
    config['debug'] = os.getenv('DEBUG').lower() == 'true'
if os.getenv('TIMEOUT'):
    config['timeout'] = int(os.getenv('TIMEOUT'))

# Note: Command-line args handled by argument parser, applied after
return config
undefined
import os from pathlib import Path import yaml
def load_config(): # 内置默认值 config = { 'debug': False, 'timeout': 30, 'color': True }
# 从配置文件加载
config_path = Path.home() / '.config' / 'myapp' / 'config.yaml'
if config_path.exists():
    with open(config_path) as f:
        file_config = yaml.safe_load(f)
        config.update(file_config)

# 用环境变量覆盖
if os.getenv('DEBUG'):
    config['debug'] = os.getenv('DEBUG').lower() == 'true'
if os.getenv('TIMEOUT'):
    config['timeout'] = int(os.getenv('TIMEOUT'))

# 注意:命令行参数由参数解析器处理,在最后应用
return config
undefined

User Interface

用户界面

Help Text

帮助文本

Provide comprehensive help:
bash
$ tool --help
Usage: tool [OPTIONS] COMMAND [ARGS]...

A brief description of what this tool does.

Options:
  -v, --verbose         Increase output verbosity
  -q, --quiet          Suppress non-error output
  -h, --help           Show this message and exit
  --version            Show version and exit

Commands:
  add                  Add a new item
  list                 List all items
  delete              Delete an item
  config              Manage configuration

Examples:
  tool add myitem
  tool list --format json
  tool delete --force

For more information: https://docs.example.com
Help text structure:
  • Usage line:
    Usage: tool [OPTIONS] COMMAND [ARGS]...
  • Brief description (1-2 lines)
  • Options section (sorted: common first)
  • Commands section (if subcommands exist)
  • Examples section (show common patterns)
  • Additional resources (docs link, contact info)
Per-command help:
bash
$ tool add --help
Usage: tool add [OPTIONS] NAME

Add a new item to the collection.

Options:
  --description TEXT  Item description
  --tags TEXT        Comma-separated tags
  --priority INT     Priority level (1-10)
  -h, --help        Show this message and exit

Examples:
  tool add "My Item"
  tool add "Task" --priority 5 --tags work,urgent
提供全面的帮助信息:
bash
$ tool --help
Usage: tool [OPTIONS] COMMAND [ARGS]...

本工具的简要功能描述。

Options:
  -v, --verbose         增加输出详细程度
  -q, --quiet          抑制非错误输出
  -h, --help           显示此帮助信息并退出
  --version            显示版本信息并退出

Commands:
  add                  添加新条目
  list                 列出所有条目
  delete              删除条目
  config              管理配置

Examples:
  tool add myitem
  tool list --format json
  tool delete --force

更多信息:https://docs.example.com
帮助文本结构:
  • 使用行:
    Usage: tool [OPTIONS] COMMAND [ARGS]...
  • 简要描述(1-2行)
  • 选项部分(按常用程度排序)
  • 命令部分(如果存在子命令)
  • 示例部分(展示常见使用模式)
  • 附加资源(文档链接、联系信息)
单命令帮助:
bash
$ tool add --help
Usage: tool add [OPTIONS] NAME

向集合中添加新条目。

Options:
  --description TEXT  条目描述
  --tags TEXT        逗号分隔的标签
  --priority INT     优先级级别(1-10)
  -h, --help        显示此帮助信息并退出

Examples:
  tool add "My Item"
  tool add "Task" --priority 5 --tags work,urgent

Version Information

版本信息

Provide version with --version / -v:
bash
$ tool --version
tool 1.2.3
通过--version / -v提供版本信息:
bash
$ tool --version
tool 1.2.3

For complex tools, show component versions

复杂工具可展示组件版本

$ tool --version tool 1.2.3 dependency-a: 2.1.0 dependency-b: 5.4.3

**Store version in configuration:**
```python
$ tool --version tool 1.2.3 dependency-a: 2.1.0 dependency-b: 5.4.3

**在配置中存储版本:**
```python

version.py or init.py

version.py 或 init.py

version = "1.2.3"
version = "1.2.3"

In main CLI file

在主CLI文件中

import sys from . import version
@click.command() @click.option('--version', is_flag=True, help='Show version and exit') def main(version): if version: click.echo(f"tool {version}") sys.exit(0)
undefined
import sys from . import version
@click.command() @click.option('--version', is_flag=True, help='显示版本信息并退出') def main(version): if version: click.echo(f"tool {version}") sys.exit(0)
undefined

Progress Indicators

进度指示器

For long operations, show progress:
python
undefined
针对长时间操作,显示进度:
python
undefined

Python: using rich library

Python: 使用rich库

from rich.progress import track import time
for i in track(range(100), description="Processing..."): time.sleep(0.1) # Long operation

**Output format:**
Processing... [████████░░] 80% or Processing... ⠏ (spinning indicator)
undefined
from rich.progress import track import time
for i in track(range(100), description="处理中..."): time.sleep(0.1) # 长时间操作

**输出格式:**
处理中... [████████░░] 80% 或 处理中... ⠏ (旋转指示器)
undefined

Spinners and Progress Bars

加载动画与进度条

Use spinners for indeterminate progress:
Connecting to server... ⠋
Uploading file... ⠙
Waiting for response... ⠹
Use progress bars for determinate progress:
Downloading [████████░░░░░░] 40% (2.1 MB / 5.3 MB)
Processing  [██████████████████░░] 90%
Implement responsibly:
  • Disable in non-interactive environments (detect piped output)
  • Respect
    --no-progress
    flag
  • Never output progress to stdout (use stderr)
针对不确定进度使用加载动画:
连接到服务器... ⠋
上传文件... ⠙
等待响应... ⠹
针对确定进度使用进度条:
下载中 [████████░░░░░░] 40% (2.1 MB / 5.3 MB)
处理中  [██████████████████░░] 90%
合理实现:
  • 在非交互式环境中禁用(检测管道输出)
  • 尊重
    --no-progress
    标志
  • 切勿将进度输出到stdout(使用stderr)

Color Output

彩色输出

Support color with disable option:
python
undefined
支持彩色输出并提供禁用选项:
python
undefined

Python: using rich or click

Python: 使用rich或click

import click
@click.command() @click.option('--color', type=click.Choice(['always', 'auto', 'never']), default='auto', help='Color output mode') def main(color): # auto: color if terminal supports it # always: force color (for piping to less -R) # never: disable color use_color = color == 'always' or (color == 'auto' and is_terminal())

**Environment variable override:**
```bash
import click
@click.command() @click.option('--color', type=click.Choice(['always', 'auto', 'never']), default='auto', help='彩色输出模式') def main(color): # auto: 终端支持则启用彩色 # always: 强制启用彩色(适用于管道到less -R) # never: 禁用彩色 use_color = color == 'always' or (color == 'auto' and is_terminal())

**环境变量覆盖:**
```bash

User can disable via environment

用户可通过环境变量禁用彩色

NO_COLOR=1 tool # Disable color FORCE_COLOR=1 tool # Force color

**Terminal detection:**
```python
import sys
import os

def supports_color():
    # Check NO_COLOR env var
    if os.getenv('NO_COLOR'):
        return False

    # Check FORCE_COLOR env var
    if os.getenv('FORCE_COLOR'):
        return True

    # Check if stdout is a TTY
    return sys.stdout.isatty()
NO_COLOR=1 tool # 禁用彩色 FORCE_COLOR=1 tool # 强制启用彩色

**终端检测:**
```python
import sys
import os

def supports_color():
    # 检查NO_COLOR环境变量
    if os.getenv('NO_COLOR'):
        return False

    # 检查FORCE_COLOR环境变量
    if os.getenv('FORCE_COLOR'):
        return True

    # 检查stdout是否为TTY
    return sys.stdout.isatty()

Interactive Prompts

交互式提示

Use for confirmations and user input:
python
undefined
用于确认操作和用户输入:
python
undefined

Python: using click

Python: 使用click

import click
@click.command() @click.option('--force', is_flag=True, help='Skip confirmations') def delete_item(force): if not force: if not click.confirm('Are you sure you want to delete?'): click.echo("Cancelled") return
# Proceed with deletion
click.echo("Deleted")
import click
@click.command() @click.option('--force', is_flag=True, help='跳过确认步骤') def delete_item(force): if not force: if not click.confirm('确定要删除吗?'): click.echo("已取消") return
# 执行删除操作
click.echo("已删除")

Interactive selection

交互式选择

choice = click.prompt( 'Choose an option', type=click.Choice(['a', 'b', 'c']), default='a' )

**Guidelines:**
- Ask for confirmation before destructive operations
- Provide sensible defaults
- Allow bypassing with `--force` flag
- Never prompt in non-interactive contexts (detect piped input)
choice = click.prompt( '选择一个选项', type=click.Choice(['a', 'b', 'c']), default='a' )

**指导原则:**
- 执行破坏性操作前要求确认
- 提供合理的默认值
- 允许通过`--force`标志跳过提示
- 切勿在非交互式上下文中弹出提示(检测管道输入)

Output Formatting

输出格式化

Human-Readable Output

人类可读输出

Default format for interactive use:
$ tool list
Name          Status    Modified
────────────────────────────────
Project A     Active    2 hours ago
Project B     Inactive  3 days ago
Project C     Active    1 week ago
Characteristics:
  • Table format with clear columns
  • Natural language (e.g., "2 hours ago")
  • Colors for emphasis (when supported)
  • Sorted intelligently
交互式使用的默认格式:
$ tool list
名称          状态    修改时间
────────────────────────────────
项目A     活跃    2小时前
项目B     不活跃  3天前
项目C     活跃    1周前
特点:
  • 表格格式,列清晰
  • 自然语言描述(例如“2小时前”)
  • 支持彩色强调(终端支持时)
  • 智能排序

Machine-Readable Output

机器可读输出

JSON format for scripting:
bash
$ tool list --format json
[
  {
    "name": "Project A",
    "status": "active",
    "modified": "2024-01-15T14:30:00Z"
  },
  {
    "name": "Project B",
    "status": "inactive",
    "modified": "2024-01-12T09:15:00Z"
  }
]
YAML format (compact, readable):
bash
$ tool list --format yaml
- name: Project A
  status: active
  modified: 2024-01-15T14:30:00Z
- name: Project B
  status: inactive
  modified: 2024-01-12T09:15:00Z
CSV format (for spreadsheets):
bash
$ tool list --format csv
name,status,modified
"Project A",active,2024-01-15T14:30:00Z
"Project B",inactive,2024-01-12T09:15:00Z
Implementation:
python
import click
import json
import csv
import io

@click.command()
@click.option('--format', type=click.Choice(['table', 'json', 'yaml', 'csv']),
              default='table', help='Output format')
def list_items(format):
    items = get_items()

    if format == 'json':
        click.echo(json.dumps(items, indent=2))
    elif format == 'csv':
        output = io.StringIO()
        writer = csv.DictWriter(output, fieldnames=items[0].keys())
        writer.writeheader()
        writer.writerows(items)
        click.echo(output.getvalue())
    elif format == 'yaml':
        import yaml
        click.echo(yaml.dump(items, default_flow_style=False))
    else:  # table
        display_table(items)
脚本化使用的JSON格式:
bash
$ tool list --format json
[
  {
    "name": "项目A",
    "status": "active",
    "modified": "2024-01-15T14:30:00Z"
  },
  {
    "name": "项目B",
    "status": "inactive",
    "modified": "2024-01-12T09:15:00Z"
  }
]
YAML格式(紧凑、可读):
bash
$ tool list --format yaml
- name: 项目A
  status: active
  modified: 2024-01-15T14:30:00Z
- name: 项目B
  status: inactive
  modified: 2024-01-12T09:15:00Z
CSV格式(适用于电子表格):
bash
$ tool list --format csv
name,status,modified
"项目A",active,2024-01-15T14:30:00Z
"项目B",inactive,2024-01-12T09:15:00Z
实现方式:
python
import click
import json
import csv
import io

@click.command()
@click.option('--format', type=click.Choice(['table', 'json', 'yaml', 'csv']),
              default='table', help='输出格式')
def list_items(format):
    items = get_items()

    if format == 'json':
        click.echo(json.dumps(items, indent=2))
    elif format == 'csv':
        output = io.StringIO()
        writer = csv.DictWriter(output, fieldnames=items[0].keys())
        writer.writeheader()
        writer.writerows(items)
        click.echo(output.getvalue())
    elif format == 'yaml':
        import yaml
        click.echo(yaml.dump(items, default_flow_style=False))
    else:  # table
        display_table(items)

Exit Codes

退出码

Standard exit codes:
0   - Success, no errors
1   - General error
2   - Misuse of shell command (invalid arguments)
3-125 - Application-specific errors
126 - Command cannot execute
127 - Command not found
128+130 - Fatal signal "N"
130 - Script terminated by Ctrl+C
Common application codes:
0   - Success
1   - General/unspecified error
2   - Misuse of command syntax
64  - Bad input data
65  - Data format error
66  - No input file
69  - Service unavailable
70  - Internal software error
71  - System error
77  - Permission denied
78  - Configuration error
Implementation:
python
import click
import sys

@click.command()
def main():
    try:
        result = do_work()
        if not result:
            sys.exit(1)  # Error condition
    except ValueError as e:
        click.echo(f"Error: {e}", err=True)
        sys.exit(2)  # Bad input
    except PermissionError as e:
        click.echo(f"Permission denied: {e}", err=True)
        sys.exit(77)
    except Exception as e:
        click.echo(f"Internal error: {e}", err=True)
        sys.exit(70)

    sys.exit(0)  # Success
Always exit explicitly:
bash
undefined
标准退出码:
0   - 成功,无错误
1   - 通用错误
2   - shell命令误用(无效参数)
3-125 - 应用程序专属错误
126 - 命令无法执行
127 - 命令未找到
128+130 - 致命信号"N"
130 - 脚本被Ctrl+C终止
常见应用程序退出码:
0   - 成功
1   - 通用/未指定错误
2   - 命令语法误用
64  - 输入数据错误
65  - 数据格式错误
66  - 无输入文件
69  - 服务不可用
70  - 内部软件错误
71  - 系统错误
77  - 权限被拒绝
78  - 配置错误
实现方式:
python
import click
import sys

@click.command()
def main():
    try:
        result = do_work()
        if not result:
            sys.exit(1)  # 错误状态
    except ValueError as e:
        click.echo(f"错误: {e}", err=True)
        sys.exit(2)  # 输入错误
    except PermissionError as e:
        click.echo(f"权限被拒绝: {e}", err=True)
        sys.exit(77)
    except Exception as e:
        click.echo(f"内部错误: {e}", err=True)
        sys.exit(70)

    sys.exit(0)  # 成功
始终显式退出:
bash
undefined

Good: script can detect success/failure

推荐:脚本可检测成功/失败

tool && echo "Success" || echo "Failed"
tool && echo "成功" || echo "失败"

Bad: unclear exit status

不推荐:退出状态不明确

tool echo "Done" # Prints regardless of success
undefined
tool echo "完成" # 无论成功与否都会输出
undefined

Error Handling

错误处理

Clear Error Messages

清晰的错误信息

Good error messages:
  • Concise (one line if possible)
  • Specific about what went wrong
  • Suggest how to fix it
bash
undefined
优质错误信息:
  • 简洁(尽可能一行)
  • 明确说明问题所在
  • 提供修复建议
bash
undefined

Good

推荐

$ tool add --priority invalid Error: --priority must be a number (1-10), got 'invalid'
$ tool add --priority invalid 错误: --priority必须是1-10之间的数字,您输入的是'invalid'

Bad

不推荐

$ tool add --priority invalid Error: Invalid argument
$ tool add --priority invalid 错误: 参数无效

Good

推荐

$ tool delete nonexistent Error: Item 'nonexistent' not found. Use 'tool list' to see available items.
$ tool delete nonexistent 错误: 条目'nonexistent'不存在。使用'tool list'查看可用条目。

Bad

不推荐

$ tool delete nonexistent File not found

**Error format:**
Error: [what happened]. [How to fix it or where to learn more].
undefined
$ tool delete nonexistent 文件未找到

**错误格式:**
错误: [发生的问题]。[修复方法或更多信息来源]。
undefined

Suggestions for Fixes

修复建议

Provide actionable suggestions:
python
import click

@click.command()
@click.argument('command')
def main(command):
    valid_commands = ['add', 'list', 'delete']

    if command not in valid_commands:
        # Suggest closest match
        from difflib import get_close_matches
        suggestions = get_close_matches(command, valid_commands, n=1)

        msg = f"Unknown command '{command}'."
        if suggestions:
            msg += f" Did you mean '{suggestions[0]}'?"
        else:
            msg += f" Available commands: {', '.join(valid_commands)}"

        click.echo(f"Error: {msg}", err=True)
        raise SystemExit(1)
提供可执行的修复建议:
python
import click

@click.command()
@click.argument('command')
def main(command):
    valid_commands = ['add', 'list', 'delete']

    if command not in valid_commands:
        # 推荐最接近的匹配项
        from difflib import get_close_matches
        suggestions = get_close_matches(command, valid_commands, n=1)

        msg = f"未知命令'{command}'。"
        if suggestions:
            msg += f" 您是不是想输入'{suggestions[0]}'?"
        else:
            msg += f" 可用命令: {', '.join(valid_commands)}"

        click.echo(f"错误: {msg}", err=True)
        raise SystemExit(1)

Debug Mode

调试模式

Provide --debug flag for detailed output:
python
import click
import sys
import traceback

@click.command()
@click.option('--debug', is_flag=True, help='Show detailed error information')
def main(debug):
    try:
        do_work()
    except Exception as e:
        if debug:
            # Show full traceback
            traceback.print_exc(file=sys.stderr)
        else:
            # Show user-friendly message
            click.echo(f"Error: {str(e)}", err=True)
            click.echo("Use --debug to see details", err=True)
        sys.exit(1)
Example output:
bash
$ tool --debug
Error: Connection failed
Traceback (most recent call last):
  File "tool.py", line 42, in main
    connect_to_server()
  File "tool.py", line 15, in connect_to_server
    raise ConnectionError("Timeout after 30s")
ConnectionError: Timeout after 30s
提供--debug标志以显示详细输出:
python
import click
import sys
import traceback

@click.command()
@click.option('--debug', is_flag=True, help='显示详细错误信息')
def main(debug):
    try:
        do_work()
    except Exception as e:
        if debug:
            # 显示完整堆栈跟踪
            traceback.print_exc(file=sys.stderr)
        else:
            # 显示用户友好的信息
            click.echo(f"错误: {str(e)}", err=True)
            click.echo("使用--debug查看详细信息", err=True)
        sys.exit(1)
示例输出:
bash
$ tool --debug
错误: 连接失败
Traceback (most recent call last):
  File "tool.py", line 42, in main
    connect_to_server()
  File "tool.py", line 15, in connect_to_server
    raise ConnectionError("30秒后超时")
ConnectionError: 30秒后超时

Cross-Platform Considerations

跨平台注意事项

Path Separators

路径分隔符

Always use proper path handling:
python
undefined
始终使用正确的路径处理方式:
python
undefined

Good: use pathlib (cross-platform)

推荐:使用pathlib(跨平台)

from pathlib import Path
config_path = Path.home() / '.config' / 'app' / 'config.yaml' output_path = Path('output') / 'result.txt'
from pathlib import Path
config_path = Path.home() / '.config' / 'app' / 'config.yaml' output_path = Path('output') / 'result.txt'

Also good: use os.path

也推荐:使用os.path

import os config_path = os.path.join(os.path.expanduser('~'), '.config', 'app', 'config.yaml')
import os config_path = os.path.join(os.path.expanduser('~'), '.config', 'app', 'config.yaml')

Avoid: hardcoded separators

避免:硬编码分隔符

config_path = '~/.config/app/config.yaml' # Wrong on Windows

**Node.js example:**
```javascript
const path = require('path');
const os = require('os');

const configPath = path.join(os.homedir(), '.config', 'app', 'config.yaml');
const outputPath = path.join('output', 'result.txt');
config_path = '~/.config/app/config.yaml' # Windows系统下无效

**Node.js示例:**
```javascript
const path = require('path');
const os = require('os');

const configPath = path.join(os.homedir(), '.config', 'app', 'config.yaml');
const outputPath = path.join('output', 'result.txt');

Line Endings

行尾

Normalize line endings:
python
undefined
规范化行尾:
python
undefined

Python: open files with universal newline support (default)

Python: 使用通用换行支持打开文件(默认设置)

with open('file.txt', 'r') as f: # Automatically handles \r\n, \n, \r content = f.read()
with open('file.txt', 'r') as f: # 自动处理\r\n、\n、\r content = f.read()

When writing, use default newline handling

写入时使用默认换行处理

with open('file.txt', 'w') as f: f.write(content) # Uses platform default newline

**Git configuration:**
```bash
with open('file.txt', 'w') as f: f.write(content) # 使用平台默认的换行符

**Git配置:**
```bash

Set in .gitattributes to normalize line endings

在.gitattributes中设置以规范化行尾

  • text=auto *.py text eol=lf *.json text eol=lf
undefined
  • text=auto *.py text eol=lf *.json text eol=lf
undefined

Terminal Capabilities

终端能力

Detect terminal capabilities:
python
import sys
import os

def get_terminal_width():
    """Get terminal width, default to 80."""
    try:
        return os.get_terminal_size().columns
    except (AttributeError, ValueError):
        return 80

def supports_unicode():
    """Check if terminal supports Unicode."""
    encoding = sys.stdout.encoding or 'utf-8'
    return encoding.lower() in ('utf-8', 'utf8')

def supports_color():
    """Check if terminal supports colors."""
    # Already covered above
    return True
检测终端能力:
python
import sys
import os

def get_terminal_width():
    """获取终端宽度,默认80列。"""
    try:
        return os.get_terminal_size().columns
    except (AttributeError, ValueError):
        return 80

def supports_unicode():
    """检查终端是否支持Unicode。"""
    encoding = sys.stdout.encoding or 'utf-8'
    return encoding.lower() in ('utf-8', 'utf8')

def supports_color():
    """检查终端是否支持彩色。"""
    # 前面已覆盖此实现
    return True

Use capabilities to adjust output

根据能力调整输出

if supports_unicode(): symbol = '✓' # Check mark else: symbol = '✔' # Alternative

**Avoid assumptions:**
```python
if supports_unicode(): symbol = '✓' # 勾选标记 else: symbol = '✔' # 替代标记

**避免假设:**
```python

Bad: assumes capabilities

不推荐:假设终端能力

output = "✓ Success\n"
output = "✓ 成功\n"

Good: checks capabilities

推荐:检查终端能力

symbol = '✓' if supports_unicode() else '✔' output = f"{symbol} Success\n"
undefined
symbol = '✓' if supports_unicode() else '✔' output = f"{symbol} 成功\n"
undefined

Testing CLI Applications

CLI应用程序测试

Integration Tests

集成测试

Test the complete CLI, not just functions:
python
import subprocess
import json

def test_list_command():
    """Test list command output."""
    result = subprocess.run(
        ['tool', 'list', '--format', 'json'],
        capture_output=True,
        text=True
    )

    assert result.returncode == 0
    output = json.loads(result.stdout)
    assert isinstance(output, list)

def test_list_command_human_readable():
    """Test list command with default format."""
    result = subprocess.run(
        ['tool', 'list'],
        capture_output=True,
        text=True
    )

    assert result.returncode == 0
    assert 'Name' in result.stdout  # Table header
Test with Click (Python):
python
from click.testing import CliRunner
from tool.cli import main

def test_list_command():
    runner = CliRunner()
    result = runner.invoke(main, ['list', '--format', 'json'])

    assert result.exit_code == 0
    output = json.loads(result.output)
    assert isinstance(output, list)
测试完整的CLI,而非仅测试函数:
python
import subprocess
import json

def test_list_command():
    """测试list命令的输出。"""
    result = subprocess.run(
        ['tool', 'list', '--format', 'json'],
        capture_output=True,
        text=True
    )

    assert result.returncode == 0
    output = json.loads(result.stdout)
    assert isinstance(output, list)

def test_list_command_human_readable():
    """测试list命令的默认格式输出。"""
    result = subprocess.run(
        ['tool', 'list'],
        capture_output=True,
        text=True
    )

    assert result.returncode == 0
    assert '名称' in result.stdout  # 表格表头
使用Click测试(Python):
python
from click.testing import CliRunner
from tool.cli import main

def test_list_command():
    runner = CliRunner()
    result = runner.invoke(main, ['list', '--format', 'json'])

    assert result.exit_code == 0
    output = json.loads(result.output)
    assert isinstance(output, list)

Output Verification

输出验证

Verify output format and content:
python
import subprocess

def test_add_command_success():
    """Test successful add operation."""
    result = subprocess.run(
        ['tool', 'add', 'Test Item', '--priority', '5'],
        capture_output=True,
        text=True
    )

    assert result.returncode == 0
    assert 'Added' in result.stdout or 'Success' in result.stdout

def test_add_command_invalid_priority():
    """Test validation of priority argument."""
    result = subprocess.run(
        ['tool', 'add', 'Test Item', '--priority', 'invalid'],
        capture_output=True,
        text=True
    )

    assert result.returncode != 0
    assert 'Error' in result.stderr
    assert 'priority' in result.stderr.lower()
验证输出格式和内容:
python
import subprocess

def test_add_command_success():
    """测试add操作成功的场景。"""
    result = subprocess.run(
        ['tool', 'add', '测试条目', '--priority', '5'],
        capture_output=True,
        text=True
    )

    assert result.returncode == 0
    assert '已添加' in result.stdout or '成功' in result.stdout

def test_add_command_invalid_priority():
    """测试priority参数的验证逻辑。"""
    result = subprocess.run(
        ['tool', 'add', '测试条目', '--priority', 'invalid'],
        capture_output=True,
        text=True
    )

    assert result.returncode != 0
    assert '错误' in result.stderr
    assert 'priority' in result.stderr.lower()

Exit Code Checks

退出码检查

Verify proper exit codes:
python
import subprocess

def test_help_flag_exit_code():
    """Test --help returns success."""
    result = subprocess.run(['tool', '--help'], capture_output=True)
    assert result.returncode == 0

def test_invalid_command_exit_code():
    """Test invalid command returns error code."""
    result = subprocess.run(['tool', 'invalid'], capture_output=True)
    assert result.returncode != 0

def test_missing_required_arg():
    """Test missing required argument returns error."""
    result = subprocess.run(['tool', 'delete'], capture_output=True)
    assert result.returncode == 2  # Misuse of command syntax
验证正确的退出码:
python
import subprocess

def test_help_flag_exit_code():
    """测试--help返回成功状态。"""
    result = subprocess.run(['tool', '--help'], capture_output=True)
    assert result.returncode == 0

def test_invalid_command_exit_code():
    """测试无效命令返回错误码。"""
    result = subprocess.run(['tool', 'invalid'], capture_output=True)
    assert result.returncode != 0

def test_missing_required_arg():
    """测试缺少必填参数返回错误。"""
    result = subprocess.run(['tool', 'delete'], capture_output=True)
    assert result.returncode == 2  # 命令语法误用

Documentation

文档

README

README

CLI tools need clear README documentation:
markdown
undefined
CLI工具需要清晰的README文档:
markdown
undefined

Tool Name

工具名称

Brief description of what the tool does.
工具的简要功能描述。

Installation

安装

Installation instructions (package manager, build from source, etc.)
安装说明(包管理器、从源码构建等)。

Usage

使用方法

Basic Usage

基础用法

bash
tool [COMMAND] [OPTIONS] [ARGUMENTS]
bash
tool [COMMAND] [OPTIONS] [ARGUMENTS]

Examples

示例

bash
undefined
bash
undefined

List all items

列出所有条目

tool list
tool list

Add new item

添加新条目

tool add "My Item" --priority 5
tool add "我的条目" --priority 5

Delete with confirmation

带确认的删除

tool delete "My Item"
tool delete "我的条目"

Suppress confirmation

跳过确认直接删除

tool delete "My Item" --force
undefined
tool delete "我的条目" --force
undefined

Commands

命令

  • add [NAME]
    - Add a new item
  • list
    - List all items
  • delete [NAME]
    - Delete an item
  • config
    - Manage configuration
  • add [NAME]
    - 添加新条目
  • list
    - 列出所有条目
  • delete [NAME]
    - 删除条目
  • config
    - 管理配置

Options

选项

  • -v, --verbose
    - Increase output verbosity
  • --format [json|yaml|csv]
    - Output format
  • --debug
    - Show debug information
  • -h, --help
    - Show help message
  • --version
    - Show version
  • -v, --verbose
    - 增加输出详细程度
  • --format [json|yaml|csv]
    - 输出格式
  • --debug
    - 显示调试信息
  • -h, --help
    - 显示帮助信息
  • --version
    - 显示版本

Configuration

配置

Settings can be configured via:
  1. Command-line flags (highest priority)
  2. Environment variables
  3. Config file at
    ~/.config/tool/config.yaml
  4. Built-in defaults
可通过以下方式配置设置:
  1. 命令行标志(最高优先级)
  2. 环境变量
  3. 配置文件
    ~/.config/tool/config.yaml
  4. 内置默认值

Environment Variables

环境变量

  • DEBUG
    - Enable debug mode
  • TOOL_CONFIG
    - Path to config file
  • NO_COLOR
    - Disable colored output
  • DEBUG
    - 启用调试模式
  • TOOL_CONFIG
    - 配置文件路径
  • NO_COLOR
    - 禁用彩色输出

Configuration File

配置文件

Create
~/.config/tool/config.yaml
:
yaml
debug: false
format: table
color: true
创建
~/.config/tool/config.yaml
yaml
debug: false
format: table
color: true

Troubleshooting

故障排除

Common Issues

常见问题

Issue: "command not found"
  • Ensure tool is installed and in PATH
  • Check:
    which tool
Issue: Permission denied
  • Make script executable:
    chmod +x tool
问题:"command not found"
  • 确保工具已安装且在PATH中
  • 检查:
    which tool
问题:Permission denied
  • 使脚本可执行:
    chmod +x tool

Development

开发

Build and test instructions.
undefined
构建和测试说明。
undefined

Man Pages

手册页

Create man pages for Unix systems:
TOOL(1)                    User Commands                    TOOL(1)

NAME
    tool - A tool that does one thing well

SYNOPSIS
    tool [OPTIONS] COMMAND [ARGS]...

DESCRIPTION
    Detailed description of what the tool does.

COMMANDS
    add NAME          Add a new item
    list             List all items
    delete NAME      Delete an item

OPTIONS
    -v, --verbose    Increase output verbosity
    --format FMT     Output format (json, yaml, csv)
    -h, --help       Show help message
    --version        Show version

EXAMPLES
    Add a new item:
        tool add "My Item"

    List items as JSON:
        tool list --format json

EXIT STATUS
    0    Success
    1    General error
    2    Invalid arguments

FILES
    ~/.config/tool/config.yaml
        Configuration file

SEE ALSO
    tool-add(1), tool-delete(1)
为Unix系统创建手册页:
TOOL(1)                    用户命令                    TOOL(1)

名称
    tool - 专注做好一件事的工具

概要
    tool [OPTIONS] COMMAND [ARGS]...

描述
    工具的详细功能描述。

命令
    add NAME          添加新条目
    list             列出所有条目
    delete NAME      删除条目

选项
    -v, --verbose    增加输出详细程度
    --format FMT     输出格式(json、yaml、csv)
    -h, --help       显示帮助信息
    --version        显示版本

示例
    添加新条目:
        tool add "我的条目"

    以JSON格式列出条目:
        tool list --format json

退出状态
    0    成功
    1    通用错误
    2    参数无效

文件
    ~/.config/tool/config.yaml
        配置文件

另请参阅
    tool-add(1), tool-delete(1)

Built-in Help

内置帮助

Make help accessible and comprehensive:
python
@click.group()
def main():
    """Main CLI tool."""
    pass

@main.command()
def help():
    """Show detailed help information."""
    click.echo(click.get_current_context().get_help())
使帮助信息易于访问且全面:
python
@click.group()
def main():
    """主CLI工具。"""
    pass

@main.command()
def help():
    """显示详细帮助信息。"""
    click.echo(click.get_current_context().get_help())

Common CLI Libraries

常见CLI库

Python

Python

Click - Most popular, decorator-based
python
import click

@click.command()
@click.option('--name', prompt='Your name', help='Name of person')
@click.option('--count', default=1, help='Number of greetings')
def hello(name, count):
    """Simple program that greets NAME COUNT times."""
    for _ in range(count):
        click.echo(f'Hello {name}!')

if __name__ == '__main__':
    hello()
Typer - Built on Click, modern with async support
python
import typer

app = typer.Typer()

@app.command()
def add(name: str, priority: int = typer.Option(5, min=1, max=10)):
    """Add a new item."""
    print(f"Added {name} with priority {priority}")

if __name__ == "__main__":
    app()
Argparse - Built-in to Python, more verbose
python
import argparse

parser = argparse.ArgumentParser(description='Process some integers')
parser.add_argument('--name', required=True, help='Name')
parser.add_argument('--count', type=int, default=1, help='Count')
args = parser.parse_args()
Click - 最受欢迎的装饰器式库
python
import click

@click.command()
@click.option('--name', prompt='您的姓名', help='用户姓名')
@click.option('--count', default=1, help='问候次数')
def hello(name, count):
    """简单的问候程序,向NAME问候COUNT次。"""
    for _ in range(count):
        click.echo(f'你好 {name}!')

if __name__ == '__main__':
    hello()
Typer - 基于Click构建,支持异步的现代库
python
import typer

app = typer.Typer()

@app.command()
def add(name: str, priority: int = typer.Option(5, min=1, max=10)):
    """添加新条目。"""
    print(f"已添加 {name},优先级为 {priority}")

if __name__ == "__main__":
    app()
Argparse - Python内置库,语法较冗长
python
import argparse

parser = argparse.ArgumentParser(description='处理一些整数')
parser.add_argument('--name', required=True, help='姓名')
parser.add_argument('--count', type=int, default=1, help='次数')
args = parser.parse_args()

Node.js / JavaScript

Node.js / JavaScript

Commander - Minimal and clean
javascript
const { Command } = require('commander');

const program = new Command();

program
  .command('add <name>')
  .option('--priority <number>', 'Item priority', '5')
  .action((name, options) => {
    console.log(`Added ${name} with priority ${options.priority}`);
  });

program.parse(process.argv);
Yargs - Feature-rich
javascript
const yargs = require('yargs/yargs');
const { hideBin } = require('yargs/helpers');

yargs(hideBin(process.argv))
  .command('add <name>', 'Add item', (yargs) => {
    return yargs.option('priority', { type: 'number', default: 5 });
  }, (argv) => {
    console.log(`Added ${argv.name} with priority ${argv.priority}`);
  })
  .argv;
Oclif - Full-featured framework for complex CLIs
javascript
const {Command, flags} = require('@oclif/command');

class AddCommand extends Command {
  static description = 'Add a new item';

  static args = [{ name: 'name' }];

  static flags = {
    priority: flags.integer({ default: 5 })
  };

  async run() {
    const { args, flags } = this.parse(AddCommand);
    this.log(`Added ${args.name} with priority ${flags.priority}`);
  }
}

module.exports = AddCommand;
Commander - 轻量简洁
javascript
const { Command } = require('commander');

const program = new Command();

program
  .command('add <name>')
  .option('--priority <number>', '条目优先级', '5')
  .action((name, options) => {
    console.log(`已添加 ${name},优先级为 ${options.priority}`);
  });

program.parse(process.argv);
Yargs - 功能丰富
javascript
const yargs = require('yargs/yargs');
const { hideBin } = require('yargs/helpers');

yargs(hideBin(process.argv))
  .command('add <name>', '添加条目', (yargs) => {
    return yargs.option('priority', { type: 'number', default: 5 });
  }, (argv) => {
    console.log(`已添加 ${argv.name},优先级为 ${argv.priority}`);
  })
  .argv;
Oclif - 适用于复杂CLI的全功能框架
javascript
const {Command, flags} = require('@oclif/command');

class AddCommand extends Command {
  static description = '添加新条目';

  static args = [{ name: 'name' }];

  static flags = {
    priority: flags.integer({ default: 5 })
  };

  async run() {
    const { args, flags } = this.parse(AddCommand);
    this.log(`已添加 ${args.name},优先级为 ${flags.priority}`);
  }
}

module.exports = AddCommand;

Go

Go

Cobra - Popular Go CLI framework
go
var rootCmd = &cobra.Command{
  Use:   "tool",
  Short: "A brief description",
  Long:  "A longer description...",
  Run: func(cmd *cobra.Command, args []string) {
    fmt.Println("Hello World!")
  },
}

var addCmd = &cobra.Command{
  Use:   "add [name]",
  Short: "Add a new item",
  Args:  cobra.MinimumNArgs(1),
  Run: func(cmd *cobra.Command, args []string) {
    fmt.Printf("Added %s\n", args[0])
  },
}

func init() {
  rootCmd.AddCommand(addCmd)
  addCmd.Flags().IntP("priority", "p", 5, "Priority level")
}
Cobra - 流行的Go CLI框架
go
var rootCmd = &cobra.Command{
  Use:   "tool",
  Short: "简要描述",
  Long:  "更详细的描述...",
  Run: func(cmd *cobra.Command, args []string) {
    fmt.Println("你好,世界!")
  },
}

var addCmd = &cobra.Command{
  Use:   "add [name]",
  Short: "添加新条目",
  Args:  cobra.MinimumNArgs(1),
  Run: func(cmd *cobra.Command, args []string) {
    fmt.Printf("已添加 %s\n", args[0])
  },
}

func init() {
  rootCmd.AddCommand(addCmd)
  addCmd.Flags().IntP("priority", "p", 5, "优先级")
}

Rust

Rust

Clap - Powerful and flexible
rust
use clap::{Parser, Subcommand};

#[derive(Parser)]
#[clap(name = "tool")]
#[clap(about = "A tool that does one thing well")]
struct Cli {
    #[clap(subcommand)]
    command: Option<Commands>,

    #[clap(short, long)]
    debug: bool,
}

#[derive(Subcommand)]
enum Commands {
    Add {
        name: String,
        #[clap(short, long, default_value = "5")]
        priority: u8,
    },
    List,
}

fn main() {
    let cli = Cli::parse();
    // Handle commands...
}
Clap - 强大灵活的库
rust
use clap::{Parser, Subcommand};

#[derive(Parser)]
#[clap(name = "tool")]
#[clap(about = "专注做好一件事的工具")]
struct Cli {
    #[clap(subcommand)]
    command: Option<Commands>,

    #[clap(short, long)]
    debug: bool,
}

#[derive(Subcommand)]
enum Commands {
    Add {
        name: String,
        #[clap(short, long, default_value = "5")]
        priority: u8,
    },
    List,
}

fn main() {
    let cli = Cli::parse();
    // 处理命令...
}

Common Patterns and Examples

常见模式与示例

Pattern: Global Options vs Subcommand Options

模式:全局选项与子命令选项

undefined
undefined

Global options apply to tool

全局选项应用于整个工具

tool --verbose add item
tool --verbose add item

Subcommand options apply to subcommand

子命令选项仅应用于子命令

tool add item --priority 5
tool add item --priority 5

Mix of both

混合使用两者

tool --verbose add item --priority 5
undefined
tool --verbose add item --priority 5
undefined

Pattern: Piping and Composition

模式:管道与组合

bash
undefined
bash
undefined

Pipe output to other tools

将输出管道到其他工具

tool list --format json | jq '.[] | select(.status == "active")'
tool list --format json | jq '.[] | select(.status == "active")'

Use as input to other commands

作为其他命令的输入

tool export > backup.json tool import < backup.json
tool export > backup.json tool import < backup.json

Chain commands

链式命令

tool list | grep important | while read item; do tool process "$item"; done
undefined
tool list | grep important | while read item; do tool process "$item"; done
undefined

Pattern: Interactive vs Non-interactive

模式:交互式与非交互式

python
import sys
import click

@click.command()
@click.option('--force', is_flag=True, help='Skip confirmations')
def delete_item(force):
    if sys.stdin.isatty() and not force:
        # Interactive - prompt for confirmation
        if not click.confirm('Delete this item?'):
            click.echo('Cancelled')
            return

    # Non-interactive or confirmed
    perform_deletion()
python
import sys
import click

@click.command()
@click.option('--force', is_flag=True, help='跳过确认步骤')
def delete_item(force):
    if sys.stdin.isatty() and not force:
        # 交互式 - 提示确认
        if not click.confirm('确定要删除此条目吗?'):
            click.echo('已取消')
            return

    # 非交互式或已确认
    perform_deletion()

Pattern: Timeout Handling

模式:超时处理

python
import signal
import sys

def timeout_handler(signum, frame):
    print("Operation timed out")
    sys.exit(124)

signal.signal(signal.SIGALRM, timeout_handler)
signal.alarm(30)  # 30 second timeout

try:
    result = long_running_operation()
finally:
    signal.alarm(0)  # Cancel alarm

Note: For project-specific CLI patterns, check
.claude/CLAUDE.md
in the project directory.
python
import signal
import sys

def timeout_handler(signum, frame):
    print("操作超时")
    sys.exit(124)

signal.signal(signal.SIGALRM, timeout_handler)
signal.alarm(30)  # 30秒超时

try:
    result = long_running_operation()
finally:
    signal.alarm(0)  # 取消超时

注意: 项目专属的CLI模式,请查看项目目录中的
.claude/CLAUDE.md