2012-04-19 4 views
3

J'essaie d'utiliser des tuyaux pour gérer une commande qui nécessite plusieurs entrées, mais je ne sais pas trop comment le faire. Voici un extrait de ce que j'essaie de faire. Je sais comment gérer la première entrée, mais je suis à perdre à la tuyauterie dans la deuxième entrée -newstdinpassNSTask avec l'entrée de plusieurs tuyaux

NSTask *task = [[NSTask alloc] init]; 
NSPipe *pipe = [NSPipe pipe]; 

[task setLaunchPath: @"/bin/sh"]; 
[task setArguments: [NSArray arrayWithObjects: @"-c", @"/usr/bin/hdiutil chpass -oldstdinpass -newstdinpass /path/to/dmg", nil]]; 
[task setStandardInput:pipe]; 
[task launch]; 

[[pipe fileHandleForWriting] writeData:[@"thepassword" dataUsingEncoding:NSUTF8StringEncoding]]; 
[[pipe fileHandleForWriting] closeFile]; 

[task waitUntilExit]; 
[task release]; 

Je sais en utilisant hdiutil de cette manière est un peu un hack, mais en termes de tuyaux , suis-je en train de le faire de la bonne façon?

Merci.

MISE À JOUR: Dans le cas où d'autres s'interrogent à ce sujet, une solution rapide à mon problème est de passer une chaîne à zéro terminal comme l'a indiqué Ken Thomases ci-dessous. Utilisez [[NSString stringWithFormat:@"oldpass\0newpass\0"] dataUsingEncoding:NSUTF8StringEncoding] dans le tuyau. Maintenant, encore besoin d'apprendre à combler plusieurs NSTasks avec des tuyaux ...

Répondre

3

Vous pouvez créer plusieurs NSTask s et un tas de NSPipe s et les accrocher ensemble, ou vous pouvez utiliser l'astuce sh -c pour alimenter une coquille d'une commande , et laissez-le analyser et mettre en place tous les IPC.

Exemple:

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

NSArray *arguments; 
arguments = [NSArray arrayWithObjects: @"-c", 
        @"cat /usr/share/dict/words | grep -i ham | rev | tail -5", nil]; 
[task setArguments: arguments]; 
// and then do all the other jazz for running an NSTask. 

Référence:http://borkware.com/quickies/one?topic=nstask


Maintenant, pour une commande fonction d'exécution "appropriée", voici que je me sers habituellement ...

Code:

/******************************************************* 
* 
* MAIN ROUTINE 
* 
*******************************************************/ 

- (void)runCommand:(NSString *)cmd withArgs:(NSArray *)argsArray 
{ 
    //------------------------------- 
    // Set up Task 
    //------------------------------- 

    if (task) { [self terminate]; } 

    task = [[NSTask alloc] init]; 
    [task setLaunchPath:cmd]; 
    [task setArguments:argsArray]; 

    [task setStandardOutput:[NSPipe pipe]]; 
    [task setStandardError:[task standardOutput]]; 

    //------------------------------- 
    // Set up Observers 
    //------------------------------- 

    [PP_NOTIFIER removeObserver:self]; 
    [PP_NOTIFIER addObserver:self 
        selector:@selector(commandSentData:) 
         name: NSFileHandleReadCompletionNotification 
         object: [[task standardOutput] fileHandleForReading]]; 

    [PP_NOTIFIER addObserver:self 
        selector:@selector(taskTerminated:) 
         name:NSTaskDidTerminateNotification 
         object:nil]; 

    //------------------------------- 
    // Launch 
    //------------------------------- 
    [[[task standardOutput] fileHandleForReading] readInBackgroundAndNotify]; 

    [task launch]; 
} 

/******************************************************* 
* 
* OBSERVERS 
* 
*******************************************************/ 

- (void)commandSentData:(NSNotification*)n 
{ 
    NSData* d; 
    d = [[n userInfo] valueForKey:NSFileHandleNotificationDataItem]; 

    if ([d length]) 
    { 
     NSString* s = [[NSString alloc] initWithData:d encoding:NSUTF8StringEncoding]; 

     NSLog(@"Received : %@",s); 
    } 

    [[n object] readInBackgroundAndNotify]; 
} 

- (void)taskTerminated:(NSNotification*)n 
{ 
    [task release]; 
    task = nil; 
} 
+1

Merci pour votre code. Je suis encore un peu confus sur la façon de raccorder plusieurs tâches ensemble pour la même commande, mais je vais prendre une dalle dessus. – Daniel

+1

Utilisez un tuyau comme sortie standard d'une tâche et l'entrée standard de la suivante. Puis un autre tuyau accrochera la sortie de la deuxième tâche à l'entrée du troisième. Etc. Si vous voulez envoyer une entrée à toute la chaîne, créez un tube et configurez-le en tant qu'entrée de la première tâche et écrivez-le comme vous l'avez déjà fait. Si vous voulez une sortie de la chaîne entière, créez un tube et définissez-le comme la sortie standard de la dernière tâche de la chaîne et lisez-la. –

+1

vous avez enregistré le jour –

2

Votre utilisation du tuyau me semble correcte.

Je ne sais pas pourquoi vous utilisez /bin/sh. Juste mettre en place le NSTask avec son chemin de lancement étant @"/usr/bin/hdiutil" et avec ses arguments étant un tableau de @"chpass", @"-oldstdinpass", @"-newstdinpass", et @"/path/to/dmg". C'est beaucoup plus sûr. Par exemple, que se passe-t-il si le chemin vers le dmg contient un caractère que l'interpréteur interprétera, par exemple $?

À moins que vous ne préfériez spécifiquement pour profiter d'une fonction shell, n'utilisez pas le shell.

+0

Ceci est un bon conseil, mais il ne traite toujours pas comment vous pouvez envoyer à la fois la valeur '-oldstdinpass' * et la valeur' -newstdinpass' via un seul tube 'stdin'. –

+0

@Ken, c'est un bon conseil.En fait, j'avais des problèmes étranges avec 'NSTask' ne fonctionnait pas correctement avec le chemin de lancement défini sur' @ "/ usr/bin/hdiutil" 'et en passant le verbe' create' comme argument. Je vais essayer la méthode non-shell pour cela. – Daniel

+2

@RobKeniger, je n'avais pas vraiment regardé les détails de la commande 'hdiutil' et comment cela prenait le mot de passe. Il prétend les prendre dans l'ordre, null-terminated (pas newline-terminé). Donc, il est possible d'écrire les deux sur le tuyau avant de le fermer. –