Есть ли способ запустить два или более NSTask?

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

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

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

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

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

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


person Jeff Escalante    schedule 07.03.2012    source источник


Ответы (4)


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

NSTask *task = [[NSTask alloc] init];
[task setLaunchPath:@"/bin/true"];
[task launch];
person mipadi    schedule 07.03.2012
comment
Я использую здесь сборку мусора и автоматический подсчет ссылок (из xcode4), поэтому я не должен использовать alloc и init... есть ли другой способ сделать это? - person Jeff Escalante; 07.03.2012
comment
Вы наверняка можете использовать alloc/init как для сборки мусора, так и для ARC. Однако нет причин использовать ARC и сборку мусора одновременно (позволит ли вам Xcode?). - person Andrew Madsen; 07.03.2012
comment
Проверяю это сейчас... в прошлом, когда я использовал alloc init, выдавало ошибку - person Jeff Escalante; 09.03.2012
comment
Все еще не работает с alloc init. Может ли кто-нибудь еще проверить это и подтвердить? - person Jeff Escalante; 09.03.2012
comment
Вы абсолютно точно используете alloc/init в приложении ARC — это сохранение/релиз/авторелиз, которые вы не можете использовать. Что вы имеете в виду под не работает? - person zpasternack; 10.03.2012

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

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 отлично работает, если захват вывода (или предоставление ввода) не требуется.

person CRD    schedule 07.03.2012
comment
Мне очень хотелось бы полностью согласиться с вами в этом вопросе, и в этом есть большой смысл. Вот как я ожидаю, что это сработает. Но вы пробовали это? Создайте два разных NSTask, которые запускают два разных скрипта, используя метод, который я сделал здесь, и попробуйте их запустить. Первый выполняется, а второй нет, независимо от того, используете ли вы alloc init или нет. Если вы можете опубликовать проверенный и работающий пример запуска двух скриптов bash один за другим, это спасет жизнь. - person Jeff Escalante; 09.03.2012
comment
@JeffEscalante - user1168440 с тех пор опубликовал код, который должен вам помочь. - person CRD; 10.03.2012
comment
@JeffEscalante - я был заинтригован, поскольку обычно использую NSTask с трубами, поэтому я попробовал самый простой из возможных launchedTaskWithLaunchPath:arguments, и он отлично работает, см. обновленный ответ. - person CRD; 10.03.2012

Вот простой подход, который я использую для запуска нескольких команд с помощью 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.
person CocoaEv    schedule 08.03.2012
comment
мило, я попробую! - person Jeff Escalante; 09.03.2012

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

person RohithKunchamwar    schedule 25.09.2014