Преобразувайте командата NSTask /bin/sh -c в подходящ код на конвейер

Може ли някой да ми помогне да конвертирам следния код в код, който вместо това има две NSTasks за "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], за да се уверя, че task1 е извела всичко, което иска да изведе първо? - person Enchilada; 06.06.2011
comment
Защото се чудя дали task2 не рискува иначе да получи преждевременен вход. - 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 изпълнява: %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