如何在一致的环境中使用NSTASK在循环中运行终端命令



我想使用nstask模拟终端以运行命令。代码如下。它可以在循环中输入并返回过程输出。

int main(int argc, const char * argv[])
{
  @autoreleasepool {      
    while (1) {
        char str[80] = {0};
        scanf("%s", str);
        NSString *cmdstr = [NSString stringWithUTF8String:str];
        NSTask *task = [NSTask new];
        [task setLaunchPath:@"/bin/sh"];
        [task setArguments:[NSArray arrayWithObjects:@"-c", cmdstr, nil]];
        NSPipe *pipe = [NSPipe pipe];
        [task setStandardOutput:pipe];
        [task launch];
        NSData *data = [[pipe fileHandleForReading] readDataToEndOfFile];
        [task waitUntilExit];
        NSString *string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
        NSLog(@"%@", string);
    }
}

我的问题是:循环结束时,运行的环境还原为初始化状态。例如,默认运行路径是/Users/apple,我运行cd /将路径更改为/,然后运行pwd,它返回/Users/apple而不是/

那么,如何使用NSTask完全模拟终端?

cdpwd是shell 内置命令。如果执行任务

/bin/sh -c "cd /"

无法将更改的工作目录返回到呼叫过程。如果要设置变量MYVAR=myvalue

也存在相同的问题。

您可以尝试分开解析这些行并更新环境。但是,

之类的多行命令呢
for file in *.txt
do
    echo $file
done

您不能通过将每行发送到单独的NSTask进程来模拟。

您唯一可以做的就是使用NSTask启动A /bin/sh进程,然后将所有输入线馈送到该过程的标准输入中。但是,您无法使用readDataToEndOfFile读取输出,但是您必须异步读取(使用[[pipe fileHandleForReading] waitForDataInBackgroundAndNotify])。

因此,简而言之:您只能通过运行(单个)外壳来模拟终端。

添加:也许您可以将以下内容用作应用程序的起点。(我省略了所有错误检查。)

int main(int argc, const char * argv[])
{
    @autoreleasepool {
        // Commands are read from standard input:
        NSFileHandle *input = [NSFileHandle fileHandleWithStandardInput];
        NSPipe *inPipe = [NSPipe new]; // pipe for shell input
        NSPipe *outPipe = [NSPipe new]; // pipe for shell output
        NSTask *task = [NSTask new];
        [task setLaunchPath:@"/bin/sh"];
        [task setStandardInput:inPipe];
        [task setStandardOutput:outPipe];
        [task launch];
        // Wait for standard input ...
        [input waitForDataInBackgroundAndNotify];
        // ... and wait for shell output.
        [[outPipe fileHandleForReading] waitForDataInBackgroundAndNotify];
        // Wait asynchronously for standard input.
        // The block is executed as soon as some data is available on standard input.
        [[NSNotificationCenter defaultCenter] addObserverForName:NSFileHandleDataAvailableNotification
                                                          object:input queue:nil
                                                      usingBlock:^(NSNotification *note)
         {
             NSData *inData = [input availableData];
             if ([inData length] == 0) {
                 // EOF on standard input.
                 [[inPipe fileHandleForWriting] closeFile];
             } else {
                 // Read from standard input and write to shell input pipe.
                 [[inPipe fileHandleForWriting] writeData:inData];
                 // Continue waiting for standard input.
                 [input 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];
             NSString *outStr = [[NSString alloc] initWithData:outData encoding:NSUTF8StringEncoding];
             NSLog(@"output: %@", outStr);
             // Continue waiting for shell output.
             [[outPipe fileHandleForReading] waitForDataInBackgroundAndNotify];
         }];
        [task waitUntilExit];
    }
    return 0;
}

最新更新