【问题标题】:How to use NSTask run terminal commands in loop in consistent environment?如何在一致的环境中循环使用 NSTask 运行终端命令?
【发布时间】:2012-11-04 09:24:59
【问题描述】:

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

【问题讨论】:

    标签: objective-c command nstask nspipe


    【解决方案1】:

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

    /bin/sh -c "cd /"
    

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

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

    这样的多行命令呢?
    for file in *.txt
    do
        echo $file
    done
    

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

    您唯一能做的就是用NSTask 启动一个单个 /bin/sh 进程,并将所有输入行提供给该进程的标准输入。但是你不能使用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;
    }
    

    【讨论】:

    • 谢谢,马丁。但是我怎样才能用NSTask 启动一个shell?你能提供一些代码示例吗?
    • @TomJacky:您将只创建并启动一次任务,并像标准输出一样使用管道作为标准输入。 - 也许你可以更具体一点你真正想做的事情。难道真的只是一个命令行程序,从标准输入中读取命令并执行命令吗?
    • 是的,这只是一个命令行程序。简而言之,我想做的是模拟终端外壳。我不知道如何在循环中读取输入命令并将这些命令提供给 std 的管道。从 std 的管道输入和读取输出。输出。那么网上有示例代码吗?
    • @TomJacky:“模拟终端”到底是什么意思?你的程序和 /bin/sh 或 /bin/bash 有什么区别?
    • 对不起,我解释得不够。没错,我正在使用 /bin/sh。 “模拟终端”的意思是我想向/bin/sh输入一个命令并得到输出,然后向它发送另一个命令并得到另一个输出,并且循环重复,就像直接与sh交互一样或重击。我不知道的是如何使用单个 NSTask 进程来处理循环命令的输入和输出?
    猜你喜欢
    • 2011-10-17
    • 2018-04-05
    • 1970-01-01
    • 1970-01-01
    • 2020-04-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多