swift sh badge-platforms badge-languages

编写 Swift 脚本是简单

$ cat <<EOF > script
#!/usr/bin/swift
print("Hi!")
EOF
$ chmod u+x script
$ ./script
Hi!

遗憾的是,要使用第三方依赖,我们必须将脚本迁移到 swift 包并使用 swift build,当我们只想快速编写一个脚本时,这是一个相对笨重的解决方案。 swift-sh 为我们提供了两全其美的方案

$ cat <<EOF > script
#!/usr/bin/swift sh
import PromiseKit  // @mxcl ~> 6.5
print(Promise.value("Hi!"))
EOF
$ chmod u+x script
$ ./script
Promise("Hi!")

如果还不清楚,swift-sh 会读取 import 后的注释,并使用此信息来获取您的依赖项。


让我们来看一个例子:如果您有一个名为 foo.swift单个文件,并且您想导入 mxcl/PromiseKit

#!/usr/bin/swift sh

import Foundation
import PromiseKit  // @mxcl ~> 6.5

firstly {
    after(.seconds(2))
}.then {
    after(.milliseconds(500))
}.done {
    print("notice: two and a half seconds elapsed")
    exit(0)
}

RunLoop.main.run()

您可以这样运行它

$ swift sh foo.swift

或者为了使其更“脚本化”,首先使其可执行

$ chmod u+x foo.swift
$ mv foo.swift foo    # optional step!

然后直接运行它

$ ./foo

赞助

如果您的公司依赖 swift-sh,请考虑赞助该项目。 否则,我很难证明维护它的合理性。

安装

brew install xcode-actions/tap/swift-sh

当前的官方 swift-sh 公式已过时,因为原始作者已停止维护它。

或者您可以使用 swift build 手动构建。

安装后会生成一个名为 swift-sh 的可执行文件,当您键入 swift sh 时,swift 可执行文件将调用此文件(前提是它在您的 PATH 中)。

我们积极支持 Linux 和 Mac,并将在 Windows 可行后立即支持 Windows。

用法

在脚本的第一行添加 shebang#!/usr/bin/swift sh

您的依赖项通过您的 import 行确定

#!/usr/bin/swift sh
import AppUpdater    // @mxcl
// ^^ https://github.com/mxcl/AppUpdater, latest version

import PromiseKit    // @mxcl ~> 6.5
// ^^ mxcl/PromiseKit, version 6.5.0 or higher up to but not including 7.0.0 or higher

import Chalk         // @mxcl == 0.3.1
// ^^ mxcl/Chalk, only version 0.3.1

import LegibleError  // @mxcl == b4de8c12
// ^^ mxcl/LegibleError, the precise commit `b4de8c12`

import Path          // mxcl/Path.swift ~> 0.16
// ^^ for when the module-name and repo-name are not identical

import BumbleButt    // https://example.com/bb.git ~> 9
// ^^ non-GitHub URLs are fine

import CommonTaDa    // git@github.com:mxcl/tada.git ~> 1
// ^^ ssh URLs are fine

import TaDa          // ssh://git@github.com:mxcl/tada.git ~> 1
// ^^ this style of ssh URL is also fine

import Foo  // ./my/project
import Bar  // ../my/other/project
import Baz  // ~/my/other/other/project
import Fuz  // /I/have/many/projects
// ^^ local dependencies must expose library products in their `Package.swift`
// careful: `foo/bar` will be treated as a GitHub dependency; prefix with `./`
// local dependencies do *not* need to be versioned


import Floibles  // @mxcl ~> 1.0.0-alpha.1
import Bloibles  // @mxcl == 1.0.0-alpha.1
// ^^ alphas/betas will only be fetched if you specify them explicitly like so
// this is per Semantic Versioning guidelines

swift-sh 读取导入后的注释并获取请求的 SwiftPM 依赖项。

无需为传递依赖项添加注释规范。

在 Xcode 中编辑

以下命令将生成一个 Xcode 项目(不在工作目录中,我们将其保存在缓存目录之外)并打开它,编辑内容将保存到您的脚本文件。

$ swift sh edit ./myScript

示例

将您的脚本转换为包

简单的脚本可以很快变成更大的项目,这些项目将受益于成为您使用 SwiftPM 构建的包。 为了帮助您迁移项目,我们提供了 swift sh eject,例如

$ swift sh eject foo.swift

./Foo 中创建一个 Swift 包,从现在开始在 Foo 目录中使用 swift build。 您的脚本现在是 ./Foo/Sources/main.swift

在 CI 中使用

如果您想使脚本可供使用 CI 的人员使用; 请使用 stdin

brew install xcode-actions/tap/swift-sh
swift sh <(curl http://example.com/yourscript) arg1 arg2

内部细节

swift sh 在 swift-sh 缓存目录 † 下方的目录中创建一个带有依赖项的 Swift Package.swift 包管理器项目,构建可执行文件,然后通过 swift run 执行它。
仅当脚本文件比可执行文件更新时,脚本才会被(重新)构建。

† 使用(FreeDesktop)环境变量 XDG_CACHE_HOME 指定缓存父目录。 如果未指定,在 macOS 上 swif-sh 使用 $HOME/Library/Developer/swift-sh.cache,否则使用 $HOME/.cache/swift-sh

Swift 版本

swfit-sh v2 需要 Swift 5.5。

swift-sh 使用活动的工具版本(即:xcode-select)或 Linux 上 PATH 中的第一个 Swift。 它为将使用该工具版本 swift build 的包编写清单。 因此,Xcode 11.0 使用 Swift 5.1 构建。 依赖项使用它们声明支持的 Swift 版本构建,前提是活动的工具链可以做到这一点(例如,Xcode 11.0 支持 Swift 4.2 及更高版本)。

要在脚本本身中声明对特定 Swift 版本的支持,请使用 #if swift#if compiler 指令。

替代方案


故障排除

error: unable to invoke subcommand: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/swift-sh

如果您是通过 Google 来到这里的,那么您有一个使用此工具的脚本,如果您现在安装 swift-sh,您将能够运行您的脚本

brew install xcode-actions/tap/swift-sh

或者参阅上面的安装说明