ios - 如何在 iOS 中使用 Swift 运行命令行命令或任务?

标签 ios swift

我正在使用 Swift 为运行 iOS 12 或更高版本的越狱设备编写 iOS 应用程序。 它是一个包管理器,为了安装包,我需要运行命令 dpkg -i [PACKAGE_ID] control

为了实现这一点,我做了以下功能:

func task(launchPath: String, arguments: String) {
    let task = CommandLine()
    task.launchPath = launchPath
    task.arguments = arguments

    let pipe = Pipe()
    task.standardOutput = pipe

    task.launch()
    task.waitUntilExit()

    let data = pipe.fileHandleForReading.readDataToEndOfFile()
    let output = String(data: data, encoding: String.Encoding.utf8)

    return output

}

当我想运行命令时,我会这样调用我的函数:

task(launchPath: "/usr/libexec/Thunderbolt/supersling", arguments: "/usr/bin/dpkg -i" + packageID + "control")

但它一直给我以下错误:

'CommandLine' cannot be constructed because it has no accessible initializers

我一直在互联网上搜索这个错误,我读到你不能在 iOS 中运行 CommandLine(),但我知道可以运行命令。

我一直在互联网上搜索如何执行此操作,并找到了一些适用于 Objective-C 的内容:

NSTask *task = [[NSTask alloc] init];
    [task setLaunchPath:@"/usr/libexec/Thunderbolt/supersling"];
    [task setArguments:@[@"/usr/bin/dpkg", @"-I", packageID, @"control"]];

    NSPipe *pipe = [NSPipe pipe];
    [task setStandardOutput:pipe];

    [task launch];
    [task waitUntilExit];

    NSFileHandle *read = [pipe fileHandleForReading];
    NSData *dataRead = [read readDataToEndOfFile];
    NSString *stringRead = [[NSString alloc] initWithData:dataRead encoding:NSUTF8StringEncoding];

这与我在 Objective-C 中所做的完全相同。

为什么 Xcode 允许运行这个 Objective-C 代码而 Swift 却不允许? 还有其他方法可以为 Swift 运行命令吗?

提前致谢。

编辑:我发现 Objective-C 代码导入了它自己的 NSTask.h 头文件:

//#import <Foundation/Foundation-Structs.h>
@class NSURL, NSArray, NSDictionary;

@interface NSTask : NSObject

@property (copy) NSURL * executableURL;
@property (copy) NSArray * arguments;
@property (copy) NSDictionary * environment;
@property (copy) NSURL * currentDirectoryURL;
@property (retain) id standardInput;
@property (retain) id standardOutput;
@property (retain) id standardError;
@property (readonly) int processIdentifier;
@property (getter=isRunning,readonly) BOOL running;
@property (readonly) int terminationStatus;
@property (readonly) long long terminationReason;
@property (copy) id terminationHandler;
@property (assign) long long qualityOfService;
+(id)currentTaskDictionary;
+(id)launchedTaskWithDictionary:(id)arg1 ;
+(id)launchedTaskWithLaunchPath:(id)arg1 arguments:(id)arg2 ;
+(id)launchedTaskWithExecutableURL:(id)arg1 arguments:(id)arg2 error:(out id*)arg3 terminationHandler:(/*^block*/id)arg4 ;
+(id)allocWithZone:(NSZone*)arg1 ;
-(void)waitUntilExit;
-(NSURL *)executableURL;
-(id)currentDirectoryPath;
-(void)setArguments:(NSArray *)arg1 ;
-(void)setCurrentDirectoryPath:(id)arg1 ;
-(id)launchPath;
-(void)setLaunchPath:(id)arg1 ;
-(int)terminationStatus;
-(long long)terminationReason;
-(void)launch;
-(BOOL)launchAndReturnError:(id*)arg1 ;
-(void)setCurrentDirectoryURL:(NSURL *)arg1 ;
-(NSURL *)currentDirectoryURL;
-(void)setExecutableURL:(NSURL *)arg1 ;
-(void)interrupt;
-(long long)suspendCount;
-(void)setStandardInput:(id)arg1 ;
-(void)setStandardOutput:(id)arg1 ;
-(void)setStandardError:(id)arg1 ;
-(id)standardInput;
-(id)standardOutput;
-(id)standardError;
-(id)init;
-(NSDictionary *)environment;
-(BOOL)isRunning;
-(BOOL)suspend;
-(BOOL)resume;
-(void)setEnvironment:(NSDictionary *)arg1 ;
-(void)setQualityOfService:(long long)arg1 ;
-(void)setTerminationHandler:(id)arg1 ;
-(int)processIdentifier;
-(id)terminationHandler;
-(long long)qualityOfService;
-(void)terminate;
-(NSArray *)arguments;
@end

我可以在 Swift 中使用它吗?如果可能,我该怎么做?

最佳答案

请阅读编辑编号3

好吧,基本上我自己想出了如何做到这一点,所以我将发布这个答案以帮助可能有同样问题的人。

< s>

请注意,这仅适用于越狱设备

这适用于未越狱和越狱的 iOS 设备

我找到的实现此目的的最佳方法是使用 custom Objective-C header file它创建了对象 NSTask 及其所需的一切。

然后,为了在 Swift 中使用此代码,您需要创建一个 Bridging-Header ,您需要在其中导入 NSTask.h 以使其暴露给 Swift 并能够在您的 Swift 代码中使用它。

完成此操作后,只需在代码中使用以下函数即可运行任务:

func task(launchPath: String, arguments: String...) -> NSString {
    let task = NSTask.init()
    task?.setLaunchPath(launchPath)
    task?.arguments = arguments

    // Create a Pipe and make the task
    // put all the output there
    let pipe = Pipe()
    task?.standardOutput = pipe

    // Launch the task
    task?.launch()
    task?.waitUntilExit()

    // Get the data
    let data = pipe.fileHandleForReading.readDataToEndOfFile()
    let output = NSString(data: data, encoding: String.Encoding.utf8.rawValue)

    return output!
}

然后这样调用它:

task(launchPath: "/usr/bin/dpkg", arguments: "-i", packageID, "control")

这也会返回值,因此您甚至可以通过执行以下操作来显示它:

print(task(launchPath: "/usr/bin/echo", arguments: "Hello, World!"))

将打印:

~> Hello, World!

希望这能解决问题。

编辑 1:我发现当您使用自定义 NSTask 对象时,您可以运行该任务,即使在未越狱的设备上也是如此。在 iPad Mini 2(iOS 12.1 ~> 越狱)和 iPhone Xr(iOS 12.2 ~> 未越狱)上测试。

注意:即使这也适用于非越狱设备,您的应用程序将在 AppStore 上被拒绝,因为 @ClausJørgensen说:

You're using private APIs, so it'll be rejected on the App Store. Also, Xcode 11 has some new functionality that will trigger a build failure when using certain private APIs.

我只建议将此用于不会上传到 App Store 的应用程序,否则,尝试在不使用命令的情况下实现您想要的,肯定会有任何其他方法可以做到这一点。

编辑 2:要使其正常工作并避免抛出 NSInternalInconsistencyException,您需要将 launchPath 设置为可执行文件的完整路径,而不仅仅是包含它的目录.

您还需要设置所有由逗号分隔的命令参数。

工作方法(2020 年 3 月 24 日)(仅适用于越狱设备)

在越狱的 iOS 设备上运行 CLI 命令有一种更简单的方法,粗略推荐而不是上面的方法。

你不能在 iOS 设备上使用 NSTask 的原因是你的应用程序是沙盒的,而且在 iOS 上没有办法避免这种情况。这就是越狱发生的地方:使用 theos ' New Instance Creator (NIC)并创建类型为 application 的新实例,您可以轻松创建非沙盒应用程序。

请注意,此过程用于制作与 Debian 打包程序一起安装的越狱包(dpkg。如果您使用过 Debian,您可能会熟悉它)。包被编译为 .deb 文件。

一旦完成,你就可以轻松地在你的应用程序上使用 NSTask,没有任何问题,就像这样:

func task(launchPath: String, arguments: String...) -> NSString {
    let task = NSTask.init()
    task?.setLaunchPath(launchPath)
    task?.arguments = arguments

    // Create a Pipe and make the task
    // put all the output there
    let pipe = Pipe()
    task?.standardOutput = pipe

    // Launch the task
    task?.launch()
    task?.waitUntilExit()

    // Get the data
    let data = pipe.fileHandleForReading.readDataToEndOfFile()
    let output = NSString(data: data, encoding: String.Encoding.utf8.rawValue)

    return output!
}

然后这样调用它:

task(launchPath: "/usr/bin/dpkg", arguments: "-i", packageID, "control")

当您完成您的应用程序后,只需使用 make 编译它:

make do #just compile the package
make package #compile the package and save it on the 'packages' folder
make package install #if theos and ssh are configured properly, compile the package and automatically install it on your iPhone.

关于ios - 如何在 iOS 中使用 Swift 运行命令行命令或任务?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56618204/

相关文章:

swift - 我的值不是 nil,而是 Getting Thread 1 : Fatal Error

swift - 在射击游戏中自动射击(Swift 4 - SpriteKit)

objective-c - swift init 在 objective-C 中不可见

ios - 实例化新 VC 后销毁当前 tabbarVC

ios - 我如何使用 dynamicType 在 swift3 中执行 "perform(selector:)"

objective-c - 有没有办法使用数学符号?

swift - 为什么我在 Swift 中为我的初始化程序不断获取 "Extra Argument in Call"

objective-c - 转换日期格式

objective-c - 如何处理屏幕底部的滑动手势

ios - 通过 CloudFront (Amazon) 下载图像