Как использовать 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 полностью смоделировать Терминал?

1 ответ

Решение

cd а также pwd являются встроенными командами оболочки Если вы выполните задачу

/bin/sh -c "cd /"

нет способа вернуть измененный рабочий каталог обратно в вызывающий процесс. Та же проблема существует, если вы хотите установить переменные MYVAR=myvalue,

Вы можете попытаться разобрать эти строки отдельно и обновить среду. Но как насчет многострочных команд, таких как

for file in *.txt
do
    echo $file
done

Вы не можете подражать этому, посылая каждую строку в отдельности NSTask процессы.

Единственное, что вы можете сделать, это начать сингл /bin/sh процесс с NSTaskи подать все входные строки на стандартный ввод этого процесса. Но тогда вы не можете использовать 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;
}
Другие вопросы по тегам