currentDirectoryPath和NSTask

时间:2014-11-21 09:17:41

标签: objective-c bash cocoa nstask

好吧,让我们说我正在创建一个(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 @"";
}

2 个答案:

答案 0 :(得分:2)

一般来说,很难从外部修改其他流程的环境和其他属性。类似地,通常不可能从外部查询那些。调试器和ps可以使用特殊权限来完成。

创建相关流程的父流程有机会在生成子流程的位置设置初始环境和属性。

cd命令必然是shell内置命令,因为它必须修改shell进程的状态。该更改不会直接影响任何其他现有流程的状态。它将由随后创建的子进程继承。

currentDirectoryPath的{​​{1}}属性仅在启动任务时有意义。它是新进程将继承的当前目录。它不跟踪子进程的当前目录,因为它不能。查询它只返回NSTask对象配置使用的值(或默认值,即创建NSTask对象的进程的当前目录)。

如果您正在尝试编写类似终端仿真器的内容,则需要创建一个长期运行的交互式shell子进程,其中包含父级和shell之间的通信管道。不要在单独的进程中运行单个命令。而是通过管道将命令写入交互式shell并读取后续输出。尝试解释该输出可能没有意义,因为它可以是一般的形式并且不容易解析。只需将其直接显示给用户即可。

或者,您必须在父进程中本地解释一些命令。这类似于shell内置函数。因此,您必须识别NSTask命令,而不是启动cd来执行它,您只需修改父进程的状态,以便新的当前目录将是用于后续任务。也就是说,您可以跟踪变量中的新当前目录,并在启动它们之前为所有后续NSTask对象设置currentDirectoryPath,或者您可以使用NSTask修改父进程的当前目录,这将是由未来的子流程自动继承。

答案 1 :(得分:1)

我相当确定在cd中运行NSTask不会更改task.currentDirectoryPath的值。您是否尝试在调度调用中设置断点以查看该值是否实际设置正确?

编辑:
从您的终止处理程序尝试执行[[APP environment] setPwd:[[[NSProcessInfo processInfo]environment]objectForKey:@"PATH"]];