如何在一致的环境中循环使用NSTask运行终端命令? [英] How to use NSTask run terminal commands in loop in consistent environment?

查看:2608
本文介绍了如何在一致的环境中循环使用NSTask运行终端命令?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想使用NSTask来模拟终端来运行命令。代码如下。

I want to use NSTask to simulate the Terminal to run commands. The codes as follows. It can get input in loop and return the process output.

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 而不是 /

My question is: when a loop is end, the running environment restore to the initialization state. For example, the default running path is /Users/apple, and I run cd / to change the path to /, and then run pwd, it return the /Users/apple rather than the /.

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

So how can I use NSTask to simulate the Terminal completely ?

推荐答案

cd pwd 是shell 内置命令。如果您执行任务

cd and pwd are shell built-in commands. If you execute the task

/bin/sh -c "cd /"

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

there is no way of getting the changed working directory back to the calling process. The same problem exists if you want to set variables MYVAR=myvalue.

您可以尝试单独解析这些行并更新环境。但是对于多行命令,如

You could try to parse these lines separately and update the environment. But what about multi-line commands like

for file in *.txt
do
    echo $file
done

您不能通过将每行发送到单独的 NSTask 处理。

You cannot emulate that by sending each line to separate NSTask processes.

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

The only thing you could do is to start a single /bin/sh process with NSTask, and feed all the input lines to the standard input of that process. But then you can not use readDataToEndOfFile to read the output, but you have to read asynchronously (using [[pipe fileHandleForReading] waitForDataInBackgroundAndNotify]).

简而言之:你可以通过运行一个shell来模拟终端。

So in short: you can simulate the Terminal only by running a (single) shell.

ADDED:也许您可以使用以下方式作为应用程式的起点。 (我已省略所有错误检查。)

ADDED: Perhaps you can use the following as a starting point for your app. (I have omitted all error checking.)

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运行终端命令?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆