使用 Swift 创建命令行工具

创建快速且内存安全的强大 CLI 工具

  • 简单易用 使用 Swift 清晰的语法和轻量级的声明式风格,快速开发功能丰富的命令行工具。
  • 功能强大 ArgumentParser 提供类型验证、丰富的帮助界面、shell 自动补全等功能,无需繁琐的配置。
  • 安全可靠 利用 Swift 的类型安全、内存安全和并发安全特性,无忧开发强大的 CLI 工具。
开始使用

使用 Argument Parser 构建简单的命令行工具

您可以使用 Swift Argument Parser 库快速构建功能完整的命令行界面。通过创建带有常规 Swift 属性的类型来定义命令。链接多个命令以创建丰富的命令层次结构。

ArgumentParser 提供详细的帮助界面、清晰的错误消息(包含近似匹配检查)、广泛的定制选项等功能。

Swift Argument Parser
import ArgumentParser
@main
struct Repeat: ParsableCommand {
  @Argument(help: "The phrase to repeat.")
  var phrase: String
  
  @Option(help: "The number of times to repeat 'phrase'.")
  var count: Int? = nil
  
  mutating func run() throws {
    let repeatCount = count ?? .max
    
    for i in 1...repeatCount {
      print(phrase)
    }
  }
}

ArgumentParser 使用示例

除了详细的帮助界面和开箱即用的错误消息外,您的 ArgumentParser CLI 工具还可以提供自动补全脚本和手册页面,以及通过接口的 JSON 渲染实现可扩展性。

$ repeat yes --count 3
yes
yes
yes

$ repeat --count
Error: Missing value for '--count <count>'
Help:  --count <count>  The number of times to repeat 'phrase'.
Usage: repeat <phrase> [--count <count>]
  See 'repeat --help' for more information.

$ repeat -h
USAGE: repeat <phrase> [--count <count>]

ARGUMENTS:
  <phrase>                The phrase to repeat.

OPTIONS:
  --count <count>         The number of times to repeat 'phrase'.
  -h, --help              Show help information.'
.\" "Generated by swift-argument-parser"
.Dd May 21, 2025
.Dt REPEAT 1
.Os
.Sh NAME
.Nm repeat
.Sh SYNOPSIS
.Nm
.Ar subcommand
.Ar phrase
.Op Fl -count Ar count
.Op Fl -help
.Sh DESCRIPTION
.Bl -tag -width 6n
.It Ar phrase
The phrase to repeat.
.It Fl -count Ar count
The number of times to repeat 'phrase'.
.It Fl h , -help
Show help information.
.It Em help
Show subcommand help information.
.Bl -tag -width 6n
.It Ar subcommands...
.El
.El
.Sh "EXIT STATUS"
.Ex -std
#compdef repeat

__repeat_complete() {
    local -ar non_empty_completions=("${@:#(|:*)}")
    local -ar empty_completions=("${(M)@:#(|:*)}")
    _describe -V '' non_empty_completions -- empty_completions -P $'\'\''
}

__repeat_custom_complete() {
    local -a completions
    completions=("${(@f)"$("${command_name}" "${@}" "${command_line[@]}")"}")
    if [[ "${#completions[@]}" -gt 1 ]]; then
        __repeat_complete "${completions[@]:0:-1}"
    fi
}

__repeat_cursor_index_in_current_word() {
    if [[ -z "${QIPREFIX}${IPREFIX}${PREFIX}" ]]; then
        printf 0
    else
        printf %s "${#${(z)LBUFFER}[-1]}"
    fi
}

_repeat() {
    emulate -RL zsh -G
    setopt extendedglob nullglob numericglobsort
    unsetopt aliases banghist

    local -xr SAP_SHELL=zsh
    local -x SAP_SHELL_VERSION
    SAP_SHELL_VERSION="$(builtin emulate zsh -c 'printf %s "${ZSH_VERSION}"')"
    local -r SAP_SHELL_VERSION

    local context state state_descr line
    local -A opt_args

    local -r command_name="${words[1]}"
    local -ar command_line=("${words[@]}")
    local -ir current_word_index="$((CURRENT - 1))"

    local -i ret=1
    local -ar arg_specs=(
        ':phrase:'
        '--count[The number of times to repeat '\''phrase'\''.]:count:'
        '(-h --help)'{-h,--help}'[Show help information.]'
    )
    _arguments -w -s -S : "${arg_specs[@]}" && ret=0

    return "${ret}"
}

_repeat
{
  "command" : {
    "arguments" : [
      {
        "abstract" : "The phrase to repeat.",
        "isOptional" : false,
        "isRepeating" : false,
        "kind" : "positional",
        "shouldDisplay" : true,
        "valueName" : "phrase"
      },
      {
        "abstract" : "The number of times to repeat 'phrase'.",
        "isOptional" : true,
        "isRepeating" : false,
        "kind" : "option",
        "names" : [
          {
            "kind" : "long",
            "name" : "count"
          }
        ],
        "preferredName" : {
          "kind" : "long",
          "name" : "count"
        },
        "shouldDisplay" : true,
        "valueName" : "count"
      },
      {
        "abstract" : "Show help information.",
        "isOptional" : true,
        "isRepeating" : false,
        "kind" : "flag",
        "names" : [
          {
            "kind" : "short",
            "name" : "h"
          },
          {
            "kind" : "long",
            "name" : "help"
          }
        ],
        "preferredName" : {
          "kind" : "long",
          "name" : "help"
        },
        "shouldDisplay" : true,
        "valueName" : "help"
      }
    ],
    "commandName" : "repeat",
    "shouldDisplay" : true,
    "subcommands" : [
      {
        "abstract" : "Show subcommand help information.",
        "arguments" : [
          {
            "isOptional" : true,
            "isRepeating" : true,
            "kind" : "positional",
            "shouldDisplay" : true,
            "valueName" : "subcommands"
          },
          {
            "isOptional" : true,
            "isRepeating" : false,
            "kind" : "flag",
            "names" : [
              {
                "kind" : "short",
                "name" : "h"
              },
              {
                "kind" : "long",
                "name" : "help"
              },
              {
                "kind" : "longWithSingleDash",
                "name" : "help"
              }
            ],
            "preferredName" : {
              "kind" : "long",
              "name" : "help"
            },
            "shouldDisplay" : false,
            "valueName" : "help"
          }
        ],
        "commandName" : "help",
        "shouldDisplay" : true,
        "superCommands" : [
          "repeat"
        ]
      }
    ]
  },
  "serializationVersion" : 0
}

让您的命令行工具更出色

Noora 是一个 Swift 包,它"将常见的命令行模式提炼成一个主题化的组件设计系统,实现更丰富和更具交互性的体验。"设计组件包括提示框、进度条和警告框等。

在 GitHub 上查看 Noora

探索使用 Swift 构建的命令行工具

  • swiftly 用于安装、管理和切换 Swift 工具链的命令行工具。 了解更多
  • DocC 用于 Swift-DocC 的命令行工具,提供生成和预览文档的支持。 了解更多
  • Swift Package Manager 用于管理 Swift 代码分发的工具,与 Swift 构建系统集成。 了解更多
  • Vapor Toolbox 用于轻松创建新的 Vapor 项目的命令行工具。 了解更多

使用 Subprocess 处理进程执行

Subprocess 是一个 Swift 库,它提供了精确、符合语言习惯的方式来启动和管理子进程。您可以异步地完整收集子进程的输出,或者使用 AsyncSequence 实时流式处理,这使得按行处理实时到达的输出变得简单。

Subprocess 让您能够精细控制环境变量、参数和许多平台特定的参数,同时充分利用 Swift 的并发特性和类型安全。无论您是在构建 CLI 工具还是服务器端 Swift 应用程序,swift-subprocess 都能完美集成。

Subprocess
import Subprocess

// Launch Nginx and monitor the log file in parallel
async let monitorResult = run(
    .path("/usr/bin/tail"),
    arguments: ["-f", "/path/to/nginx.log"]
) { execution, standardOutput in
    for try await line in standardOutput.lines(encoding: UTF8.self) {
        // Parse the log text
        if line.contains("500") {
            // Oh no, 500 error
        }
    }
}

let launchResult = try await run(
    .name("nginx"), // Lookup executable by name
    arguments: ["-c", "/path/to/nginx.conf"]
)
if !launchResult.terminationStatus.isSuccess {
    print("Nginx failed to launch: \(launchResult.terminationStatus)")
} else {
    print("Nginx launched with PID \(launchResult.processIdentifier)")
}