ios - 如何在 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
我一直在互联网上搜索此错误,并且我读到您无法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 运行命令?
提前致谢。
编辑:我发现 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
好的,所以基本上我自己想出了如何做到这一点,所以我将发布这个答案以帮助可能有同样问题的人。
请注意,这仅适用于越狱设备
这适用于非越狱和越狱的iOS设备
我能找到的实现这一点的最佳方法是使用自定义的 Objective-C 头文件,它创建了对象 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 ~> Jailbroken ) 和 iPhone Xr ( iOS 12.2 ~> not jailbroken ) 上测试。
注意:即使这也适用于非越狱设备,您的应用程序将在 AppStore 上被拒绝,正如@ClausJørgensen所说:
您正在使用私有 API,因此它会在 App Store 上被拒绝。此外,Xcode 11 有一些新功能会在使用某些私有 API 时触发构建失败。
我只建议将它用于不会上传到 App Store 的应用程序,否则,尝试在不使用命令的情况下实现你想要的,肯定会有任何其他方式来做到这一点。
编辑 2:为此工作并避免抛出NSInternalInconsistencyException
,您需要将 launchPath 设置为可执行文件的完整路径,而不仅仅是包含它的目录。
您还需要设置以逗号分隔的所有命令参数。
工作方法(2020 年 3 月 24 日)(仅限越狱设备)
在越狱的 iOS 设备上运行 CLI 命令有一种更简单的方法,大致推荐使用该方法而不是上面的方法。
你不能NSTask
在 iOS 设备上使用的原因是你的应用程序是沙盒的,没有办法在股票 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.
推荐阅读
- .htaccess - .htaccess 和 session 不能正常工作
- roku - 除了 Observefield 之外,Roku 中的自定义回调
- oracle - Oracle EM Express 未显示所有选项
- python - Django 更改未反映在浏览器上
- python - 伙计们,我是 python 新手,请帮我理解为什么我的代码给我“none”作为输出?
- amazon-web-services - 在 AWS ECS 服务中调用 system.exit() 是否会导致任务(服务管理器)重新启动?
- windows - 创建注册表项 SID REG_BINARY
- c# - 运行时程序集绑定重定向
- html - 计算的边框宽度小于 div 中指定的边框宽度
- docker - 安装 VECTR 时出现 docker-compose 支持问题