我正在使用 NSTask 来像终端一样执行 shell 命令。我想在 root 级别运行该命令,因此我将 sh 切换到 root 用户。这工作正常,直到我想中断正在运行的进程(如 ping 命令)。下面是我的代码,
#import "ViewController.h"
@interface ViewController()
{
NSTask* task;
}
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self setupTask];
[self executeCommand:@"sudo -S -i"];
[self executeCommand:@"password"];
[self executeCommand:@"ping www.google.com"];
[self performSelector:@selector(terminateTask) withObject:nil afterDelay:3];
}
-(void)setupTask {
@autoreleasepool {
NSPipe *inPipe = [NSPipe new]; // pipe for shell input
NSPipe *outPipe = [NSPipe new]; // pipe for shell output
NSPipe* errorPipe = [NSPipe new];
task = [NSTask new];
[task setLaunchPath:@"/bin/sh"]; //
[task setStandardInput:inPipe];
[task setStandardOutput:outPipe];
[task setStandardError:errorPipe];
[task launch];
// ... and wait for shell output.
[[outPipe fileHandleForReading] waitForDataInBackgroundAndNotify];
[[errorPipe fileHandleForReading] waitForDataInBackgroundAndNotify];
// Wait asynchronously for shell output.
// The block is executed as soon as some data is available on the shell output pipe.
[[NSNotificationCenter defaultCenter] addObserverForName:NSFileHandleDataAvailableNotification
object:[outPipe fileHandleForReading] queue:nil
usingBlock:^(NSNotification *note)
{
// Read from shell output
NSData *outData = [[outPipe fileHandleForReading] availableData];
if ([outData length] > 0) {
NSString *outStr = [[NSString alloc] initWithData:outData encoding:NSUTF8StringEncoding];
if ([outStr isEqualToString:@"Password:"]) {
NSLog(@"Password asked");
[[outPipe fileHandleForReading] waitForDataInBackgroundAndNotify];
} else {
NSLog(@"output: %@", outStr);
}
// Continue waiting for shell output.
}
[[outPipe fileHandleForReading] waitForDataInBackgroundAndNotify];
}];
}
}
-(void)executeCommand:(NSString*)command {
[task resume];
NSData* data = [command dataUsingEncoding:NSUTF8StringEncoding];
NSError* error;
[[[task standardInput] fileHandleForWriting] writeData:data error:&error];
if (error != NULL) {
NSLog(@"%@", [error localizedDescription]);
}
NSData* newLine = [@"\n" dataUsingEncoding:NSUTF8StringEncoding];
[[[task standardInput] fileHandleForWriting] writeData:newLine error:&error];
if (error != NULL) {
NSLog(@"%@", [error localizedDescription]);
}
}
-(void)terminateTask {
[task interrupt];
}
@end
如何在根模式下运行命令并处理中断、挂起等信号?
显然,
sudo -i
命令打开了另一个bash,因此信号没有被传递。我已经在根级别启动了我的应用程序,因此 bash 默认情况下将在根级别启动,我不必手动更改为 sudo。使用 sudo -i
的另一个缺点是用户可以使用 exit
命令退出 sudo 模式,这是我不想要的。因此,从根级别启动 bash 是正确的解决方案。