【问题标题】:How to pass parameters via NSUserUnixTask and then access via Script?如何通过 NSUserUnixTask 传递参数,然后通过脚本访问?
【发布时间】:2019-03-04 19:57:05
【问题描述】:

我正在开发一项功能,以启用将由 Mac 应用程序执行的用户提供的脚本。

NSUserScriptTask 是脚本调用代码的基础,NSUserAppleScriptTaskNSUserAutomatorTask 子类都允许设置变量以将信息从 Swift 传递到脚本:

Passing variables to an AppleScript

Setting NSUserAutomatorTask variables without requiring Automator Workflows to declare that variable

剩下的NSUserUnixTask 不支持设置变量。相反,它支持称为arguments[String] 数组。

执行脚本时,我想从 Mac 应用程序传递 3 个变量:

let folderURL: String? = "/files/"
let fileURLs: [String] = ["/files/file1", "/files/file2"]
let selectionType: Int? = 1

let arguments: [String] = ["how", "should", "arguments", "be", "formatted", "?"]

let unixScript = try! NSUserUnixTask(url: url)
unixScript.execute(withArguments: arguments) { (error) in
    if let error = error {
        print(error)
    }
}

这 3 个 swift 变量必须压缩成一个 [String] 数组,以便 NSUserUnixTask 用作其 arguments 参数。

当脚本运行时,我想以原型方式为脚本作者提供对相同参数的访问权限:

#! /bin/bash 
say "How should the script then access/parse the arguments?"
say $@ #says the arguments

基于脚本作者的易用性,Swift 代码应该如何将其信息格式化为参数[String]

可以提供哪些样板代码以允许从脚本中轻松实用地访问参数?

【问题讨论】:

  • @Inian 我将提供开发人员启动脚本所需的参数,并从 Swift 代码中传递所需的参数。我需要以对脚本开发人员有用的方式格式化所述参数+任何标志等。我不确定这种格式应该是什么样子。这就是 bash 脚本标记在这里的关系。如果您有任何建议,希望得到您的推荐。
  • 您能否也为此类参数和标志添加一些实际值,并让您知道您希望bash 如何解析它们。这些信息有点太模糊,无法提供有意义的答案。我真的可以帮助更多这样的例子
  • @Inian 这确实是我要问的问题:我不知道我应该提供哪些标志或应该如何构造或解析参数。我有问题中所示的 Swift 参数。我想以惯用的格式向脚本开发人员提供这些参数。
  • 当您的意思是对脚本开发人员,那么来自 swift 代码的参数将由bash 脚本处理?你有没有以一些的方式让它传递给这个bash 脚本?如果不是,您可以尝试以某种方式显示它是如何在bash 脚本中接收到的吗?
  • 如果您能告知一些不正确的方式当前将参数传递给bash 脚本,我可以提供改进或建议

标签: swift bash macos shell scripting


【解决方案1】:

有很多方法可以做到这一点,这取决于 shell 脚本程序员的期望。如果我是他们中的一员,我会要求你保持简单:

  • 位置参数(开始时强制参数的固定数量)
  • 可选的命名参数(-flag value 样式的任何参数)
  • 更多参数(可变数量的附加参数)

按照您的示例,稍作修改:

import Foundation

let shellScript = CommandLine.arguments[1]

let folderURL: String = "/files/"
let fileURLs: [String] = ["/files/file1", "/files/file2"]
let selectionType: Int? = 1

var arguments = [String]()

// script.sh <folder> [-type <type>] file1, file2, ...

arguments.append(folderURL)  // <folder> is mandatory

if let type = selectionType {    // -type <type> is optional
    arguments.append("-type")   
    arguments.append("\(type)")
}

arguments += fileURLs  // file1, ... (if it can't be empty check it here)

assert(FileManager.default.fileExists(atPath: shellScript))
let unixScript = try! NSUserUnixTask(url: URL(fileURLWithPath: shellScript))
let stdout = FileHandle.standardOutput
unixScript.standardOutput = stdout

unixScript.execute(withArguments: arguments) { error in
    if let error = error {
        print("Failed: ", error)
    }
    exit(0)
}

dispatchMain()  // I'm not swift script expert, there may be a better way

以及对应的shell脚本:

#!/bin/bash

# mandatory positional arguments

FOLDER=$1 && shift
# other mandatory arguments goes here

TYPE=7  # default type
FILES=()

while [[ $# -gt 0 ]]; do
    arg=$1
    case $arg in
        -type) 
            TYPE=$2 && shift && shift
            ;;
        # other named parameters here
        *)
            FILES+=($1) && shift
            ;;
    esac
done

echo FOLDER: '<'${FOLDER}'>'
echo TYPE: '<'${TYPE}'>'
echo FILES: '<'${FILES[@]}'>'

exit 0

例子:

ScriptRunner /path/to/script.sh

输出:

FOLDER: </files/>
TYPE: <1>
FILES: </files/file1 /files/file2>

我使用 swift 包管理器构建:

// swift-tools-version:4.2

import PackageDescription

let package = Package(
    name: "ScriptRunner",
    dependencies: [],
    targets: [
        .target(name: "ScriptRunner", dependencies: [])
    ]
)

【讨论】:

  • 这太完美了,谢谢你的详细回答。分配参数的开关很棒。
猜你喜欢
  • 2023-03-05
  • 2014-07-31
  • 1970-01-01
  • 2015-10-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-12-20
相关资源
最近更新 更多