├── .github ├── FUNDING.yml └── workflows │ ├── ci.yml │ └── homebrew.yml ├── .gitignore ├── .travis.yml ├── Examples ├── README.md ├── async-main-count-lines ├── ink └── terminal-rainbow ├── LICENSE.md ├── Package.resolved ├── Package.swift ├── Package@swift-5.3.swift ├── README.md ├── Sources ├── Command │ ├── CommandLine.usage.swift │ ├── clean().swift │ ├── edit().swift │ ├── editor().swift │ ├── eject().swift │ ├── run().Input.swift │ └── run().swift ├── Script │ ├── Constraint.swift │ ├── DependencyName.swift │ ├── ExecutableTargetMainStyle.swift │ ├── ImportSpecification.swift │ ├── Script.swift │ └── parse().swift ├── Utility │ ├── Path+ResolvedHash.swift │ ├── Process+1up.swift │ ├── TemporaryDirectory.swift │ ├── etc.swift │ └── exec().swift ├── main.swift └── swift-sh-edit │ └── main.swift ├── Tests ├── All │ ├── ImportSpecificationUnitTests.swift │ ├── IntegrationTests.swift │ ├── ModeUnitTests.swift │ ├── UtilityTests.swift │ └── XCTestManifests.swift └── LinuxMain.swift └── tea.yaml /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: mxcl 2 | patreon: mxcl 3 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | pull_request: 4 | paths: 5 | - '**.swift' 6 | - .github/workflows/ci.yml 7 | schedule: 8 | - cron: '3 3 * * 0' # 3:03 AM, every Sunday 9 | jobs: 10 | macOS: 11 | runs-on: ${{ matrix.cfg.macos }} 12 | strategy: 13 | matrix: 14 | cfg: 15 | - swift: 5.5 16 | macos: macos-12 17 | - swift: 5.6 18 | macos: macos-12 19 | - swift: 5.7 20 | macos: macos-12 21 | - swift: ~5.8 22 | macos: macos-14 23 | - swift: ~5.9 24 | macos: macos-14 25 | - swift: ~5.10 26 | macos: macos-14 27 | # - swift: ~6.0 28 | # macos: macos-14 29 | steps: 30 | - uses: actions/checkout@v4 31 | - uses: mxcl/xcodebuild@v3 32 | with: 33 | swift: ${{ matrix.cfg.swift }} 34 | linux: 35 | runs-on: ubuntu-20.04 36 | strategy: 37 | matrix: 38 | swift: 39 | # - 5.1.5 # requires ubuntu 18.04 40 | - 5.2.5 41 | - 5.3.3 42 | - 5.4.3 43 | - 5.5.3 44 | - 5.6.3 45 | - 5.7.3 46 | - 5.8.1 47 | - 5.9.2 48 | - 5.10.1 49 | steps: 50 | - uses: actions/checkout@v4 51 | - uses: YOCKOW/Action-setup-swift@v1 52 | with: 53 | swift-version: ${{ matrix.swift }} 54 | - run: swift test -Xswiftc -suppress-warnings --enable-test-discovery 55 | -------------------------------------------------------------------------------- /.github/workflows/homebrew.yml: -------------------------------------------------------------------------------- 1 | name: Homebrew Bump Formula 2 | on: 3 | release: 4 | types: [published] 5 | jobs: 6 | core: 7 | runs-on: macos-latest 8 | steps: 9 | - uses: dawidd6/action-homebrew-bump-formula@v3 10 | with: 11 | token: ${{ secrets.PAT }} 12 | formula: swift-sh 13 | mxcl: 14 | runs-on: macos-latest 15 | steps: 16 | - uses: dawidd6/action-homebrew-bump-formula@v3 17 | with: 18 | tap: mxcl/homebrew-made 19 | token: ${{ secrets.PAT }} 20 | formula: swift-sh 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /swift-sh.xcodeproj 2 | /sync 3 | /.swiftpm 4 | /.build 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | os: osx 2 | language: swift 3 | 4 | if: type = pull_request 5 | 6 | jobs: 7 | include: 8 | - osx_image: xcode11.3 # macOS 10.14.6 9 | - osx_image: xcode12.2 # macOS 10.15.7 10 | - osx_image: xcode12.3 # macOS 11.1 11 | - osx_image: xcode12.4 # macOS 11.2.1 12 | - osx_image: xcode12.5 # macOS 11.3 13 | 14 | script: swift test --enable-test-discovery 15 | -------------------------------------------------------------------------------- /Examples/README.md: -------------------------------------------------------------------------------- 1 | * [Single-script Webserver](https://github.com/cak/server.swift/blob/master/server.swift) 2 | * [Delete tweets](https://gist.github.com/mxcl/002c3514d50b73287c89268c45662394) 3 | * [Terminal Rainbow](terminal-rainbow) 4 | * [Ink example](ink) 5 | 6 | Add your script here via PR. 7 | -------------------------------------------------------------------------------- /Examples/async-main-count-lines: -------------------------------------------------------------------------------- 1 | #!/user/bin/swift sh 2 | import Foundation 3 | import ArgumentParser // apple/swift-argument-parser ~> 1.4.0 4 | 5 | /// example borrowed from [here](https://swiftpackageindex.com/apple/swift-argument-parser/1.4.0/documentation/argumentparser/asyncparsablecommand) 6 | @main 7 | struct CountLines: AsyncParsableCommand { 8 | @Argument(transform: URL.init(fileURLWithPath:)) 9 | var inputFile: URL 10 | mutating func run() async throws { 11 | let fileHandle = try FileHandle(forReadingFrom: inputFile) 12 | let lineCount = try await fileHandle.bytes.lines.reduce(into: 0) 13 | { count, _ in count += 1 } 14 | print(lineCount) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Examples/ink: -------------------------------------------------------------------------------- 1 | #!/usr/bin/swift sh 2 | 3 | import Ink // @JohnSundell == 0.5.0 4 | 5 | let markdown = """ 6 | # Ink parses markdown and renders to HTML 7 | ## Features 8 | - header blocks 9 | - list blocks 10 | - nested list 11 | - Character styles 12 | - *italic* 13 | - **bold** 14 | - ~~strikethrough~~ 15 | 16 | ## HTML output: 17 | 18 | 19 | """ 20 | let html = MarkdownParser().html(from: markdown) 21 | print(markdown) 22 | print(html) 23 | -------------------------------------------------------------------------------- /Examples/terminal-rainbow: -------------------------------------------------------------------------------- 1 | #!/usr/bin/swift sh 2 | import Foundation 3 | import Chalk // @mxcl == 0.4.0 4 | 5 | extension Int { 6 | var fg: UInt8 { 7 | if !(16..<250 ~= self) || 24...36 ~= (self - 16) % 36 { 8 | return 16 9 | } else { 10 | return 255 11 | } 12 | } 13 | var bg: UInt8 { 14 | return UInt8(self) 15 | } 16 | var paddedString: String { 17 | return " \(self)".padding(toLength: 5, withPad: " ", startingAt: 0) 18 | } 19 | var terminator: String { 20 | return (self + 3).isMultiple(of: 6) ? "\n" : "" 21 | } 22 | } 23 | 24 | for x in 0...255 { 25 | print("\(x.paddedString, color: .extended(x.fg), background: .extended(x.bg))", terminator: x.terminator) 26 | } 27 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Unlicense (Public Domain) 2 | ============================ 3 | 4 | This is free and unencumbered software released into the public domain. 5 | 6 | Anyone is free to copy, modify, publish, use, compile, sell, or 7 | distribute this software, either in source code form or as a compiled 8 | binary, for any purpose, commercial or non-commercial, and by any 9 | means. 10 | 11 | In jurisdictions that recognize copyright laws, the author or authors 12 | of this software dedicate any and all copyright interest in the 13 | software to the public domain. We make this dedication for the benefit 14 | of the public at large and to the detriment of our heirs and 15 | successors. We intend this dedication to be an overt act of 16 | relinquishment in perpetuity of all present and future rights to this 17 | software under copyright law. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 22 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 23 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 24 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 25 | OTHER DEALINGS IN THE SOFTWARE. 26 | 27 | For more information, please refer to <> 28 | -------------------------------------------------------------------------------- /Package.resolved: -------------------------------------------------------------------------------- 1 | { 2 | "object": { 3 | "pins": [ 4 | { 5 | "package": "AEXML", 6 | "repositoryURL": "https://github.com/tadija/AEXML.git", 7 | "state": { 8 | "branch": null, 9 | "revision": "38f7d00b23ecd891e1ee656fa6aeebd6ba04ecc3", 10 | "version": "4.6.1" 11 | } 12 | }, 13 | { 14 | "package": "CryptoSwift", 15 | "repositoryURL": "https://github.com/krzyzanowskim/CryptoSwift", 16 | "state": { 17 | "branch": null, 18 | "revision": "4b0565384d3c4c588af09e660535b2c7c9bf5b39", 19 | "version": "1.4.2" 20 | } 21 | }, 22 | { 23 | "package": "LegibleError", 24 | "repositoryURL": "https://github.com/mxcl/LegibleError", 25 | "state": { 26 | "branch": null, 27 | "revision": "3aeb15455edc0cd6ea0e8d562079db4ab9a25afc", 28 | "version": "1.0.5" 29 | } 30 | }, 31 | { 32 | "package": "Path.swift", 33 | "repositoryURL": "https://github.com/mxcl/Path.swift", 34 | "state": { 35 | "branch": null, 36 | "revision": "9c6f807b0a76be0e27aecc908bc6f173400d839e", 37 | "version": "1.4.0" 38 | } 39 | }, 40 | { 41 | "package": "PathKit", 42 | "repositoryURL": "https://github.com/kylef/PathKit.git", 43 | "state": { 44 | "branch": null, 45 | "revision": "3bfd2737b700b9a36565a8c94f4ad2b050a5e574", 46 | "version": "1.0.1" 47 | } 48 | }, 49 | { 50 | "package": "Spectre", 51 | "repositoryURL": "https://github.com/kylef/Spectre.git", 52 | "state": { 53 | "branch": null, 54 | "revision": "26cc5e9ae0947092c7139ef7ba612e34646086c7", 55 | "version": "0.10.1" 56 | } 57 | }, 58 | { 59 | "package": "StreamReader", 60 | "repositoryURL": "https://github.com/mxcl/StreamReader", 61 | "state": { 62 | "branch": null, 63 | "revision": "0cabaa9d539037da1b25b8cbe58a8ecafaedea67", 64 | "version": "1.0.1" 65 | } 66 | }, 67 | { 68 | "package": "Version", 69 | "repositoryURL": "https://github.com/mxcl/Version", 70 | "state": { 71 | "branch": null, 72 | "revision": "1fe824b80d89201652e7eca7c9252269a1d85e25", 73 | "version": "2.0.1" 74 | } 75 | }, 76 | { 77 | "package": "XcodeProj", 78 | "repositoryURL": "https://github.com/tuist/xcodeproj", 79 | "state": { 80 | "branch": null, 81 | "revision": "446f3a0db73e141c7f57e26fcdb043096b1db52c", 82 | "version": "8.3.1" 83 | } 84 | } 85 | ] 86 | }, 87 | "version": 1 88 | } 89 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.1 2 | import PackageDescription 3 | 4 | let package = Package( 5 | name: "swift-sh", 6 | platforms: [ 7 | .macOS(.v10_12) 8 | ], 9 | products: [ 10 | .executable(name: "swift-sh", targets: ["swift-sh"]), 11 | .library(name: "Script", targets: ["Script"]), 12 | .library(name: "Utility", targets: ["Utility"]), 13 | .library(name: "Command", targets: ["Command"]), 14 | ], 15 | dependencies: [ 16 | .package(url: "https://github.com/mxcl/Path.swift", from: "1.0.1"), 17 | .package(url: "https://github.com/mxcl/StreamReader", from: "1.0.0"), 18 | .package(url: "https://github.com/mxcl/LegibleError", from: "1.0.0"), 19 | .package(url: "https://github.com/mxcl/Version", from: "2.0.0"), 20 | .package(url: "https://github.com/krzyzanowskim/CryptoSwift", "1.3.0"..<"1.4.0"), 21 | ], 22 | targets: [ 23 | .target( 24 | name: "swift-sh", 25 | dependencies: ["Command", "LegibleError"], 26 | path: "Sources", 27 | exclude: ["Script", "Utility", "Command", "swift-sh-edit"], 28 | sources: ["main.swift"]), 29 | .target( 30 | name: "Script", 31 | dependencies: ["Utility", "StreamReader"]), 32 | .target( 33 | name: "Utility", 34 | dependencies: [ 35 | .product(name: "Path", package: "Path.swift"), 36 | "Version", 37 | "CryptoSwift" 38 | ]), 39 | .target( 40 | name: "Command", 41 | dependencies: ["Script"]), 42 | .testTarget( 43 | name: "All", 44 | dependencies: ["swift-sh"]), 45 | ] 46 | ) 47 | 48 | #if os(macOS) 49 | package.products.append(.executable(name: "swift-sh-edit", targets: ["swift-sh-edit"])) 50 | package.targets.append(.target(name: "swift-sh-edit", dependencies: ["XcodeProj", "Utility"])) 51 | package.dependencies.append(.package(url: "https://github.com/tuist/xcodeproj", from: "7.0.0")) 52 | #endif 53 | -------------------------------------------------------------------------------- /Package@swift-5.3.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.3 2 | import PackageDescription 3 | 4 | let package = Package( 5 | name: "swift-sh", 6 | platforms: [ 7 | .macOS(.v10_12) 8 | ], 9 | products: [ 10 | .executable(name: "swift-sh", targets: ["swift-sh"]), 11 | .library(name: "Script", targets: ["Script"]), 12 | .library(name: "Utility", targets: ["Utility"]), 13 | .library(name: "Command", targets: ["Command"]), 14 | ], 15 | dependencies: [ 16 | .package(url: "https://github.com/mxcl/Path.swift", from: "1.0.1"), 17 | .package(url: "https://github.com/mxcl/StreamReader", from: "1.0.0"), 18 | .package(url: "https://github.com/mxcl/LegibleError", from: "1.0.0"), 19 | .package(url: "https://github.com/mxcl/Version", from: "2.0.0"), 20 | .package(url: "https://github.com/krzyzanowskim/CryptoSwift", from: "1.4.2"), 21 | ], 22 | targets: [ 23 | .target( 24 | name: "swift-sh", 25 | dependencies: ["Command", "LegibleError"], 26 | path: "Sources", 27 | exclude: ["Script", "Utility", "Command", "swift-sh-edit"], 28 | sources: ["main.swift"]), 29 | .target( 30 | name: "Script", 31 | dependencies: ["Utility", "StreamReader"]), 32 | .target( 33 | name: "Utility", 34 | dependencies: [ 35 | .product(name: "Path", package: "Path.swift"), 36 | "Version", 37 | "CryptoSwift" 38 | ]), 39 | .target( 40 | name: "Command", 41 | dependencies: ["Script"]), 42 | .testTarget( 43 | name: "All", 44 | dependencies: ["swift-sh"]), 45 | ] 46 | ) 47 | 48 | #if os(macOS) 49 | package.products.append(.executable(name: "swift-sh-edit", targets: ["swift-sh-edit"])) 50 | package.targets.append(.target(name: "swift-sh-edit", dependencies: ["XcodeProj", "Utility"])) 51 | package.dependencies.append(.package(name: "XcodeProj", url: "https://github.com/tuist/xcodeproj", from: "8.3.1")) 52 | #endif 53 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # `swift sh` ![badge-platforms] ![badge-languages] [![Build Status](https://travis-ci.com/mxcl/swift-sh.svg)](https://travis-ci.com/mxcl/swift-sh) 2 | 3 | Writing Swift scripts is *easy*: 4 | 5 | ```sh 6 | $ cat < script 7 | #!/usr/bin/swift 8 | print("Hi!") 9 | EOF 10 | $ chmod u+x script 11 | $ ./script 12 | Hi! 13 | ``` 14 | 15 | Sadly, to use third-party dependencies we have to migrate our script to a swift 16 | package and use `swift build`, a relatively heavy solution when all we wanted 17 | was to whip up a quick script. `swift-sh` gives us the best of both worlds: 18 | 19 | ```sh 20 | $ cat < script 21 | #!/usr/bin/swift sh 22 | import PromiseKit // @mxcl ~> 6.5 23 | print(Promise.value("Hi!")) 24 | EOF 25 | $ chmod u+x script 26 | $ ./script 27 | Promise("Hi!") 28 | ``` 29 | 30 | In case it’s not clear, `swift-sh` reads the comment after the `import` and 31 | uses this information to fetch your dependencies. 32 | 33 | --- 34 | 35 | Let’s work through an example: if you had a *single file* called `foo.swift` 36 | and you wanted to import [mxcl/PromiseKit](https://github.com/mxcl/PromiseKit): 37 | 38 | ```swift 39 | #!/usr/bin/swift sh 40 | 41 | import Foundation 42 | import PromiseKit // @mxcl ~> 6.5 43 | 44 | firstly { 45 | after(.seconds(2)) 46 | }.then { 47 | after(.milliseconds(500)) 48 | }.done { 49 | print("notice: two and a half seconds elapsed") 50 | exit(0) 51 | } 52 | 53 | RunLoop.main.run() 54 | ``` 55 | 56 | You could run it with: 57 | 58 | ``` 59 | $ swift sh foo.swift 60 | ``` 61 | 62 | Or to make it more “scripty”, first make it executable: 63 | 64 | ``` 65 | $ chmod u+x foo.swift 66 | $ mv foo.swift foo # optional step! 67 | ``` 68 | 69 | And then run it directly: 70 | 71 | ``` 72 | $ ./foo 73 | ``` 74 | 75 | # Sponsorship 76 | 77 | If your company depends on `swift-sh` please consider sponsoring the project. 78 | Otherwise it is hard for me to justify maintaining it. 79 | 80 | # Installation 81 | 82 | ``` 83 | brew install swift-sh 84 | ``` 85 | 86 | Or you can build manually using `swift build`. 87 | 88 | Installation results in a single executable called `swift-sh`, the `swift` 89 | executable will call this (provided it is in your `PATH`) when you type: 90 | `swift sh`. 91 | 92 | We actively support both Linux and Mac and will support Windows as soon as it is 93 | possible to do so. 94 | 95 | # Usage 96 | 97 | Add the *shebang* as the first line in your script: `#!/usr/bin/swift sh`. 98 | 99 | Your dependencies are determined via your `import` lines: 100 | 101 | ```swift 102 | #!/usr/bin/swift sh 103 | import AppUpdater // @mxcl 104 | // ^^ https://github.com/mxcl/AppUpdater, latest version 105 | 106 | import PromiseKit // @mxcl ~> 6.5 107 | // ^^ mxcl/PromiseKit, version 6.5.0 or higher up to but not including 7.0.0 or higher 108 | 109 | import Chalk // @mxcl == 0.3.1 110 | // ^^ mxcl/Chalk, only version 0.3.1 111 | 112 | import LegibleError // @mxcl == b4de8c12 113 | // ^^ mxcl/LegibleError, the precise commit `b4de8c12` 114 | 115 | import Path // mxcl/Path.swift ~> 0.16 116 | // ^^ for when the module-name and repo-name are not identical 117 | 118 | import BumbleButt // https://example.com/bb.git ~> 9 119 | // ^^ non-GitHub URLs are fine 120 | 121 | import CommonTaDa // git@github.com:mxcl/tada.git ~> 1 122 | // ^^ ssh URLs are fine 123 | 124 | import TaDa // ssh://git@github.com:mxcl/tada.git ~> 1 125 | // ^^ this style of ssh URL is also fine 126 | 127 | import Foo // ./my/project 128 | import Bar // ../my/other/project 129 | import Baz // ~/my/other/other/project 130 | import Fuz // /I/have/many/projects 131 | // ^^ local dependencies must expose library products in their `Package.swift` 132 | // careful: `foo/bar` will be treated as a GitHub dependency; prefix with `./` 133 | // local dependencies do *not* need to be versioned 134 | 135 | 136 | import Floibles // @mxcl ~> 1.0.0-alpha.1 137 | import Bloibles // @mxcl == 1.0.0-alpha.1 138 | // ^^ alphas/betas will only be fetched if you specify them explicitly like so 139 | // this is per Semantic Versioning guidelines 140 | ``` 141 | 142 | `swift-sh` reads the comments after your imports and fetches the requested 143 | SwiftPM dependencies. 144 | 145 | It is not necessary to add a comment specification for transitive dependencies. 146 | 147 | # Editing in Xcode 148 | 149 | The following will generate an Xcode project (not in the working directory, we 150 | keep it out the way in our cache directory) and open it, edits are saved to your 151 | script file. 152 | 153 | ``` 154 | $ swift sh edit ./myScript 155 | ``` 156 | 157 | # Examples 158 | 159 | * [Tweet deleter](https://gist.github.com/mxcl/002c3514d50b73287c89268c45662394) 160 | * [PostgreSQL Check](https://gist.github.com/joscdk/c4b89add26509c6dfabf84974e62543d) 161 | 162 | # Converting your script to a package 163 | 164 | Simple scripts can quickly become bigger projects that would benefit from being 165 | packages that you build with SwiftPM. To help you migrate your project we 166 | provide `swift sh eject`, for example: 167 | 168 | $ swift sh eject foo.swift 169 | 170 | creates a Swift package in `./Foo`, from now on use `swift build` in the 171 | `Foo` directory. Your script is now `./Foo/Sources/main.swift`. 172 | 173 | # Use in CI 174 | 175 | If you want to make scripts available to people using CI; use `stdin`: 176 | 177 | brew install mxcl/made/swift-sh 178 | swift sh <(curl http://example.com/yourscript) arg1 arg2 179 | 180 | # Internal Details 181 | 182 | `swift sh` creates a Swift `Package.swift` package manager project with 183 | dependencies in a directory below the swift-sh cache directory †, 184 | builds the executable, and then executes it via `swift run`. 185 | The script is (only) rebuilt when the script file is newer than the executable. 186 | 187 | † Specify the cache parent directory using the (FreeDesktop) environment 188 | variable XDG_CACHE_HOME. If unspecified, on macOS `swif-sh` uses 189 | `$HOME/Library/Developer/swift-sh.cache`, and otherwise it uses 190 | `$HOME/.cache/swift-sh`. 191 | 192 | # Swift Versions 193 | 194 | `swfit-sh` v2 requires Swift 5.1. We had to drop support for Swift v4.2 195 | because maintenance was just too tricky. 196 | 197 | `swift-sh` uses the active tools version, (ie: `xcode-select`) or whichever 198 | Swift is first in the `PATH` on Linux. It writes a manifest for the package 199 | it will `swift build` with that tools-version. Thus Xcode 11.0 builds with Swift 5.1. 200 | Dependencies build with the Swift versions they declare support for, provided 201 | the active toolchain can do that (eg. Xcode 11.0 supports Swift 4.2 and above) 202 | 203 | To declare a support for specific Swift versions in your script itself, use 204 | `#if swift` or `#if compiler` directives. 205 | 206 | # Alternatives 207 | 208 | * [Beak](https://github.com/yonaskolb/Beak) 209 | * [Marathon](https://github.com/JohnSundell/Marathon) 210 | 211 | --- 212 | 213 | # Troubleshooting 214 | 215 | ### `error: unable to invoke subcommand: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/swift-sh` 216 | 217 | If you got here via Google, you have a script that uses this tool, if you now 218 | install `swift-sh`, you will be able to run your script: 219 | 220 | brew install mxcl/made/swift-sh 221 | 222 | Or see the [above installation instructions](#Installation). 223 | 224 | [badge-platforms]: https://img.shields.io/badge/platforms-macOS%20%7C%20Linux-lightgrey.svg 225 | [badge-languages]: https://img.shields.io/badge/swift-5.1%20%7C%205.2%20%7C%205.3%20%7C%205.4%20%7C%205.5%20%7C%205.6%20%7C%205.7%20%7C%205.8%20%7C%205.9%20%7C%206.0-orange.svg 226 | -------------------------------------------------------------------------------- /Sources/Command/CommandLine.usage.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import Path 3 | 4 | //MARK: CommandLine.usage 5 | 6 | public extension CommandLine { 7 | static var usage: String { 8 | var rv = """ 9 | swift sh