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

2024-03-19

我正在用 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

我一直在互联网上搜索此错误,并且我读到您无法运行CommandLine()在 iOS 中,但我知道可以运行命令。

我一直在互联网上搜索如何做到这一点,并找到了一些适用于 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 命令吗?

提前致谢。

EDIT:我发现 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

Ok, so basically I figured out by myself how to do this, so I'm going to post this answer in order to help people who might have the same problem.

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

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

我能找到的实现这一目标的最佳方法是使用自定义 Objective-C 头文件 https://pastebin.com/V28wJq6A它创建了对象 NSTask 及其所需的一切。

然后,为了在 Swift 中使用此代码,您需要创建一个桥接头 https://developer.apple.com/documentation/swift/imported_c_and_objective-c_apis/importing_objective-c_into_swift,您需要在其中导入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!

希望这能解决问题。

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

NOTE:尽管这也适用于非越狱设备,但您的​​应用程序将在 AppStore 上被拒绝,因为@克劳斯约根森 https://stackoverflow.com/users/95309/claus-j%C3%B8rgensen said:

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 的应用程序使用此功能,否则,请尝试在不使用命令的情况下实现您想要的目标,肯定会有其他方法可以做到这一点。

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

您还需要设置所有命令参数,以commas.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何在 iOS 中使用 Swift 运行命令行命令或任务? 的相关文章

随机推荐