How to use NSTask run terminal commands in loop in consistent environment?

蹲街弑〆低调 提交于 2019-12-21 06:27:18

问题


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);

    }
}

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 /.

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


回答1:


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

/bin/sh -c "cd /"

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

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

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]).

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

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;
}


来源:https://stackoverflow.com/questions/13217415/how-to-use-nstask-run-terminal-commands-in-loop-in-consistent-environment

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!