Преобразуйте команду NSTask /bin/sh -c в правильный код конвейера.

Может ли кто-нибудь помочь мне преобразовать следующий код в код, который вместо этого имеет два NSTask для «cat» и «grep», показывая, как их можно соединить вместе с помощью каналов? Я предполагаю, что предпочел бы последний подход, так как тогда мне больше не нужно беспокоиться о цитировании и прочем.

NSTask *task;
task = [[NSTask alloc] init];
[task setLaunchPath: @"/bin/sh"];

NSArray *arguments;
arguments = [NSArray arrayWithObjects: @"-c",
             @"cat /usr/share/dict/words | grep -i ham", nil];
[task setArguments: arguments];
[task launch];

Обновление: обратите внимание, что cat и grep здесь приведены просто как (паршивый) пример. Я все еще хочу сделать это для команд, которые имеют больше смысла.


person Enchilada    schedule 05.06.2011    source источник
comment
Что касается стиля команд оболочки: по возможности избегайте cat. grep принимает несколько файловых аргументов после шаблона, поэтому вам следует использовать grep -i ham /usr/share/dict/words. И смотрите: ваша проблема исчезает, так как вы можете запускать grep напрямую и больше не нуждаетесь в конвейере.   -  person Jeremy W. Sherman    schedule 05.06.2011


Ответы (1)


Используйте экземпляр NSTask для каждой программы и соедините их стандартные входы/выходы с NSPipe:

NSPipe *pipe = [[NSPipe alloc] init];
NSPipe *resultPipe = [[NSPipe alloc] init];

NSTask *task1 = [[NSTask alloc] init];
[task1 setLaunchPath: @"/bin/cat"];
[task1 setStandardOutput: pipe];
[task1 launch];

NSTask *task2 = [[NSTask alloc] init];
[task2 setLaunchPath: @"/bin/grep"];
[task2 setStandardInput: pipe];
[task2 setStandardOutput: resultPipe];
[task2 launch];

NSData *result = [[resultPipe fileHandleForReading] readDataToEndOfFile];
person Sven    schedule 05.06.2011
comment
Вы уверены, что мне не нужно выполнять [task1 waitUntilExit], чтобы убедиться, что задача1 вывела все, что она хочет вывести первой? - person Enchilada; 06.06.2011
comment
Потому что мне интересно, не рискует ли в противном случае задача2 получить преждевременный ввод. - person Enchilada; 06.06.2011
comment
Нет, трубка позаботится об этом. На самом деле вы не можете дождаться завершения первой задачи, чтобы начать вторую, поскольку буферное пространство в канале ограничено. - person Sven; 06.06.2011
comment
Но если я хочу запустить этот код синхронно (т. е. подождать, пока он не будет выполнен, прежде чем делать что-либо еще), теперь важна задача2, верно? Должен ли я ввести [task2 waitUntilExit] после вашего кода, а затем получить данные из вывода task2 (через новый канал, который я установлю с помощью [task2 setStandardOutput:newPipe])? - person Enchilada; 06.06.2011
comment
А также, если я использую task2 только в конце, после того как [task2 waitUntilExit] будет выполнено, также гарантируется, что task1 будет выполнена, верно? Тогда я мог бы проверить статус возврата из обеих задач, верно? - person Enchilada; 06.06.2011
comment
Вы не должны использовать NSTask метод waitUntilExit в этой ситуации. Я обновил свой пример кода, чтобы прочитать вывод второй задачи. - person Sven; 06.06.2011
comment
Но если я попытаюсь выполнить NSLog(@task1 running: %d,[task1 isRunning]); NSLog(@задача2 запущена: %d,[задача2 выполняется]); после твоего нового кода вижу, что результат непредсказуем. Задача 1 всегда не выполняется (т. е. завершена). Однако задача 2 иногда все еще выполняется, а иногда нет. Вы уверены, что я не хочу [task2 waitUntilExit]; после вашего нового кода? - person Enchilada; 06.06.2011
comment
После readDataToEndOfFile вторая задача закрыла канал. Процесс может все еще выполняться, но он не может больше выводить данные, поэтому нет смысла ждать завершения процесса. - person Sven; 06.06.2011
comment
Если только вы не хотите убедиться, что вещь вернула 0 (успех), верно? - person Enchilada; 06.06.2011
comment
Правда, если вам нужно возвращаемое значение дочернего процесса, вам нужно дождаться его завершения, я должен был упомянуть об этом раньше. - person Sven; 06.06.2011