2

我想使用 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完全模拟终端呢?

4

1 回答 1

6

cd并且pwd是 shell内置命令。如果你执行任务

/bin/sh -c "cd /"

无法将更改后的工作目录返回到调用进程。如果要设置变量,也会存在同样的问题MYVAR=myvalue

您可以尝试分别解析这些行并更新环境。但是多行命令呢?

for file in *.txt
do
    echo $file
done

您无法通过将每一行发送到单独的NSTask进程来模拟这一点。

您唯一能做的就是使用 启动单个 /bin/sh进程NSTask,并将所有输入行提供给该进程的标准输入。但是你不能使用readDataToEndOfFile读取输出,但你必须异步读取(使用[[pipe fileHandleForReading] waitForDataInBackgroundAndNotify])。

简而言之:您只能通过运行(单个)shell 来模拟终端。

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

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;
}
于 2012-11-04T11:44:42.923 回答