好的,假设我正在创建一个(Bash)终端仿真器--我实际上不是,但它在描述方面非常接近。
我设法让(几乎)所有东西都正常工作,但是我面临一个简单的问题:维护当前目录。
我是说..。假设用户运行pwd
,我们通过NSTask
和/usr/bin/env bash
执行这一操作。这将输出当前应用程序的目录。这很好。
现在,假设用户输入cd ..
。这条路在变,对吧?(好吧,即使是那个特定的会议,但它正在改变,不是吗?)
因此,我想在任务终止时存储任务的currentDirectoryPath
,然后在启动另一个与Bash相关的任务时重新设置它。
然而,它总是得到完全相同的路径(应用程序包所在的路径)。
我遗漏了什么?
有什么办法让这件事起作用吗?
代码
- (NSString*)exec:(NSArray *)args environment:(NSDictionary*)env action:(void (^)(NSString*))action completed:(void (^)(NSString*))completed
{
_task = [NSTask new];
_output = [NSPipe new];
_error = [NSPipe new];
_input = [NSPipe new];
NSFileHandle* outputF = [_output fileHandleForReading];
NSFileHandle* errorF = [_error fileHandleForReading];
__block NSString* fullOutput = @"";
NSMutableDictionary* envs = [NSMutableDictionary dictionary];
envs[@"PATH"] = [[APP environment] envPath];
[_task setLaunchPath:@"/usr/bin/env"];
if (env)
{
for (NSString* key in env)
{
envs[key] = env[key];
}
}
[_task setEnvironment:envs];
[_task setArguments:args];
[_task setStandardOutput:_output];
[_task setStandardError:_error];
[_task setStandardInput:_input];
void (^outputter)(NSFileHandle*) = ^(NSFileHandle *file){
NSData *data = [file availableData];
NSString* str = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
action(str);
fullOutput = [fullOutput stringByAppendingString:str];
};
[outputF setReadabilityHandler:outputter];
[errorF setReadabilityHandler:outputter];
[_task setTerminationHandler:^(NSTask* task){
completed(fullOutput);
dispatch_async(dispatch_get_main_queue(), ^{
[[APP environment] setPwd:[task currentDirectoryPath]];
});
[task.standardOutput fileHandleForReading].readabilityHandler = nil;
[task.standardError fileHandleForReading].readabilityHandler = nil;
[task.standardInput fileHandleForWriting].writeabilityHandler = nil;
[task terminate];
task = nil;
}];
if (![[[APP environment] pwd] isEqualToString:@""])
[_task setCurrentDirectoryPath:[[APP environment] pwd]];
[_task launch];
return @"";
}
发布于 2014-11-21 21:50:32
一般情况下,很难从外部修改另一个过程的环境和其他属性。同样,一般也不可能从外部查询这些内容。调试器和ps
可以通过使用特殊权限来完成这一任务。
创建相关进程的父进程有机会在生成子进程的地方设置初始环境和属性。
cd
命令必然是一个shell内置命令,因为它必须修改shell进程的状态。该更改不会直接影响任何其他现有进程的状态。它将由随后创建的子进程继承。
只有在任务启动时,NSTask
的NSTask
属性才有意义。新进程将继承的是当前目录。它不跟踪子进程的当前目录,因为它不能。查询它只返回NSTask
对象被配置使用的值(或者缺省值,即创建NSTask
对象的进程的当前目录)。
如果您试图编写类似于终端仿真器的内容,则需要创建一个长时间运行的交互式shell子进程,其中包含父进程和外壳之间的通信管道。不要在单独的进程中运行单独的命令。相反,通过管道将命令写入交互式shell并读取后续输出。尝试解释输出可能是没有意义的,因为它在形式上是通用的,而且不易被解析。直接显示给用户就行了。
或者,您必须在父进程中本地解释一些命令。这将类似于外壳内置。因此,您必须识别一个cd
命令,而不是启动一个NSTask
来执行它,而是修改父进程的状态,以便将新的当前目录用于后续任务。也就是说,您可以在变量中跟踪新的当前目录,并在启动这些对象之前为所有后续的NSTask
对象设置NSTask
,或者可以使用-[NSFileManager changeCurrentDirectoryPath:]
修改父进程的当前目录,该目录将由未来的子进程自动继承。
发布于 2014-11-21 10:24:25
我相当肯定,在cd
中运行NSTask
不会改变task.currentDirectoryPath
的值。您是否尝试过在调度调用中设置一个断点,以查看该值是否确实被正确设置?
编辑:
从终止处理程序中尝试执行[[APP environment] setPwd:[[[NSProcessInfo processInfo]environment]objectForKey:@"PATH"]];
https://stackoverflow.com/questions/27057837
复制相似问题