2012-03-07 3 views
1

Вы можете запустить скрипт в приложении Cocoa, используя замечательный NSTask, и он отлично работает. Единственная проблема заключается в том, что мне нужно запустить несколько сценариев, а в моем приложении сценарии не могут быть объединены в один файл или один вызов - они должны запускаться как отдельные задачи приложением.Есть ли способ запустить две или более NSTasks?

Проблема заключается в том, что, по-видимому, вы можете запускать только один из NSTask в приложении. Я не понимаю, почему это так, но, к сожалению, похоже. Я пробовал все, чтобы отлаживать его, но независимо от того, какой сценарий, насколько простой или сложный, мое приложение просто выполнит только первый NSTask, который я запускаю. This problem has come up before, хотя и менее прямо, и, похоже, не было решения.

Должен быть способ запуска нескольких сценариев в приложении. Кто-нибудь знает способ, которым я могу обойти это, или, возможно, альтернативный способ запуска сценария? Все, что мне нужно сделать, это запустить очень короткий скрипт bash, который выполняет «make install».

Вот пример того, как я запускаю NSTask, если это помогает.

NSTask *task; 
task = [NSTask launchedTaskWithLaunchPath: @"/bin/bash" 
           arguments:[NSArray arrayWithObjects: scriptPath, nil] 
     ]; 

Это действительно работает для всех моих скриптов в отдельности, он просто не может запускать один другой.

ответ

5

Уверены, что вы можете использовать более одного NSTask. Просто используйте свой метод init вместо способа его удобства, и установить свойства вручную:

NSTask *task = [[NSTask alloc] init]; 
[task setLaunchPath:@"/bin/true"]; 
[task launch]; 
+0

Я использую сбор мусора и автоматический подсчет ссылок здесь (от xcode4), поэтому я не должен использовать alloc и init ... есть ли какой-либо другой способ сделать это возможным? –

+1

Вы можете, безусловно, использовать alloc/init как с сборкой мусора, так и с ARC. Тем не менее, нет причин использовать ARC и сборку мусора одновременно (будет ли Xcode даже позволять вам?). –

+0

Тестирование этого сейчас ... в прошлом, когда я использовал alloc init, он бросил мне ошибку –

1

Существует некоторая путаница здесь над «выполняется только один раз».

NSTask создает новый процесс, это созданный процесс, который может запускаться только один раз; вы можете использовать NSTask для создания как можно большего количества различных процессов, и каждый из них может запускаться один раз. И каждый из этих создаваемых вами разностных процессов может выполнять разные или одинаковые двоичные файлы, поэтому, если вы хотите создать много процессов, каждый из которых выполняет /bin/bash, вы можете.

Для каждого из ваших сценариев создайте NSTask и запустите его.

Примечание: метод класса launchedTaskWithLaunchPath:arguments создает NSTask и запускает процесс. Вы также можете создать NSTask с [NSTask new] (или [[NSTask alloc] init]), установить его параметры и запустить его.

Примечания: от вашего комментария на другой ответ, используя [NSTask new] или [[NSTask alloc] init] является нормальным и вы делаете это, используетесь ли сбор мусора, автоматический подсчет ссылок или ручной подсчет ссылок. Это методы retain, release и autorelease, которые вы не используете для сбора мусора и автоматического подсчета ссылок.

Ответ на комментарий

user1168440 показал более общий метод вызова NSTask, но так же, как подтверждение использования метода класса здесь две оболочки скрипты исполнялись.Сначала создайте два сценария в /tmp:

tmp $ cat script1.sh 
#!/bin/bash 
echo `date -u` script one >>/tmp/script.txt 
tmp $ cat script2.sh 
#!/bin/bash 
echo `date -u` script two >>/tmp/script.txt 

И тривиальное приложение Obj-C:

#import "TasksAppDelegate.h" 

@implementation TasksAppDelegate 

@synthesize window; 

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification 
{ 
    [NSTask launchedTaskWithLaunchPath:@"/bin/bash" arguments:[NSArray arrayWithObject:@"/tmp/script1.sh"]]; 
    [NSTask launchedTaskWithLaunchPath:@"/bin/bash" arguments:[NSArray arrayWithObject: @"/tmp/script2.sh"]]; 
} 

@end 

Выполнить в Xcode и проверьте результат:

tmp $ cat script.txt 
Sat Mar 10 05:12:43 UTC 2012 script two 
Sat Mar 10 05:12:43 UTC 2012 script one 

Обе задачи были бежать, так это не то, что остановило ваш код. Может быть, один из ваших сценариев оболочки, необходимых для чтения со стандартного ввода, не прошел и, похоже, не запускался вообще?

В общем, способ, который устанавливает трубу, является выходом для захвата (и может подавать вход), является гораздо лучшим способом использования NSTask, но launchedTaskWithLaunchPath:arguments работает отлично, если захват вывода (или подачи ввода) не требуется.

+0

Я был бы очень соблазнен согласиться с вами полностью на этом, и это имеет большой смысл. Так я и ожидал, что это сработает. Но вы пробовали это? Создайте два разных NSTasks, которые запускают два разных сценария, используя метод, который я здесь сделал, и пытаюсь запустить их. Первый выполняется, а второй - нет, используете ли вы init или нет. Если вы можете опубликовать тестовый и рабочий пример запуска двух сценариев bash, один за другим, это будет спасателем жизни –

+0

@JeffEscalante - user1168440 с тех пор разместил некоторый код, который должен вам помочь. – CRD

+0

@JeffEscalante - Я был заинтригован, поскольку я обычно использую 'NSTask' с трубами, поэтому я попробовал самый простой' launchTaskWithLaunchPath: arguments', и он отлично работает, см. Обновленный ответ. – CRD

0

Вот простой метод, который я использую для запуска нескольких команд с с NSTask:

First setup a method to accept your commands 
-(NSString *) runForOutput:(NSString *)command { 

//initialize the command line to run 
NSString *runCmd = [[NSString alloc] initWithString:@"/bin/bash"]; 
NSArray *runArgs = [[NSArray alloc] initWithObjects:@"-c",command,nil]; 

//update proper label 
return [self runThisCmd:runCmd withArgs:runArgs]; 
} 

next setup a method to call NSTask 
-(NSString *) runThisCmd:(NSString *) runString withArgs:(NSArray *)runArgs { 

NSTask *task = [NSTask new]; 
[task setLaunchPath:runString]; 
[task setArguments:runArgs]; 
[task setStandardOutput:[NSPipe pipe]]; 
[task setStandardInput: [NSPipe pipe]]; 
[task setStandardError: [task standardOutput]]; 
[task launch]; 

NSData *stdOuput = [[[task standardOutput] fileHandleForReading] readDataToEndOfFile]; 
[task waitUntilExit]; 
NSString *outputString = [[NSString alloc] initWithData:stdOuput encoding:NSUTF8StringEncoding]; 
return outputString; 
} 

Now run a bunch of commands 
- (void) runSomething { 

    NSString *hostnameLong = [self runForOutput:@"hostname"]; 
    NSString *hostnameShort = [self runForOutput:@"hostname -s"]; 
    NSString *startDate = [self runForOutput:@"date '+Start Date: %Y-%m-%d'"]; 
    NSString *startTime = [self runForOutput:@"date '+Start Time: %H:%M:%S'"]; 

} 

The output of the commands are in the strings. You can work with it from there. 
This method is best for short, quick command lines due to the [task waitUntilExit] 
call that will block. This works great to simulate what I call "unix one-liners". 
I use it sometimes from awakeFromNib to grab one to two bits of info I may need. 
For long running scripts you would convert to an async approach and use notifications to update variables. 
+0

сладкий, я сделаю это! –

-1

попытка использовать концепцию многопоточности. Назначив каждый NStask в другом потоке, вы можете запустить два или более NStasks.

Смежные вопросы