207 votes

Exécuter une commande terminal à partir d'une application Cocoa

Comment puis-je exécuter une commande de terminal (comme grep ) depuis mon application Objective-C Cocoa ?

2 votes

Je ne fais que constater l'évidence : avec le sandboxing, vous ne pouvez pas lancer des applications qui ne sont pas dans votre sandbox ET elles doivent être signées par vous pour le permettre.

0 votes

@Daij-Djan ce n'est pas vrai du tout, du moins pas sous macOS. Une application macOS sandboxée peut exécuter n'importe quel binaire dans des endroits tels que /usr/bingrep vies.

1 votes

Non. S'il vous plaît, prouvez-moi que j'ai tort ;) sur ist nstask échouera à exécuter tout ce qui n'est pas dans votre bac à sable.

285voto

Gordon Wilson Points 14721

Vous pouvez utiliser [NSTask](http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSTask_Class/Reference/Reference.html) . Voici un exemple d'exécution de ' /usr/bin/grep foo bar.txt '.

int pid = [[NSProcessInfo processInfo] processIdentifier];
NSPipe *pipe = [NSPipe pipe];
NSFileHandle *file = pipe.fileHandleForReading;

NSTask *task = [[NSTask alloc] init];
task.launchPath = @"/usr/bin/grep";
task.arguments = @[@"foo", @"bar.txt"];
task.standardOutput = pipe;

[task launch];

NSData *data = [file readDataToEndOfFile];
[file closeFile];

NSString *grepOutput = [[NSString alloc] initWithData: data encoding: NSUTF8StringEncoding];
NSLog (@"grep returned:\n%@", grepOutput);

NSPipe et NSFileHandle sont utilisés pour rediriger la sortie standard de la tâche.

Pour des informations plus détaillées sur l'interaction avec le système d'exploitation depuis votre application Objective-C, vous pouvez consulter ce document sur le Development Center d'Apple : Interagir avec le système d'exploitation .

Edit : Inclus un correctif pour le problème NSLog

Si vous utilisez NSTask pour exécuter un utilitaire de ligne de commande via bash, alors vous devez inclure cette ligne magique pour que NSLog fonctionne :

//The magic line that keeps your log where it belongs
task.standardOutput = pipe;

Une explication est ici : https://web.archive.org/web/20141121094204/https://cocoadev.com/HowToPipeCommandsWithNSTask

0 votes

Merci beaucoup. Si je dois ajouter des options comme -e, dois-je les ajouter au tableau des arguments également ?

1 votes

Yup, 'arguments = [NSArray arrayWithObjects : @ "-e", @ "foo", @ "bar.txt", nil];'.

0 votes

N'oubliez pas de terminer l'appel à arrayWithObjects par un nil :)

44voto

Kenial Points 548

l'article de kent m'a donné une nouvelle idée. cette méthode runCommand n'a pas besoin d'un fichier script, il suffit de lancer une commande par une ligne :

- (NSString *)runCommand:(NSString *)commandToRun
{
    NSTask *task = [[NSTask alloc] init];
    [task setLaunchPath:@"/bin/sh"];

    NSArray *arguments = [NSArray arrayWithObjects:
                          @"-c" ,
                          [NSString stringWithFormat:@"%@", commandToRun],
                          nil];
    NSLog(@"run command:%@", commandToRun);
    [task setArguments:arguments];

    NSPipe *pipe = [NSPipe pipe];
    [task setStandardOutput:pipe];

    NSFileHandle *file = [pipe fileHandleForReading];

    [task launch];

    NSData *data = [file readDataToEndOfFile];

    NSString *output = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
    return output;
}

Vous pouvez utiliser cette méthode comme suit :

NSString *output = runCommand(@"ps -A | grep mysql");

1 votes

Elle gère bien la plupart des cas, mais si vous l'exécutez en boucle, elle finit par lever une exception en raison d'un trop grand nombre de gestionnaires de fichiers ouverts. Cela peut être corrigé en ajoutant : [file closeFile] ; après readDataToEndOfFile.

0 votes

@DavidStein : Je pense que l'utilisation de autoreleasepool pour envelopper la méthode runCommand semble être plutôt que. En fait, le code ci-dessus ne prend pas en compte les non-ARC.

0 votes

@Kenial : Oh, c'est une bien meilleure solution. Elle libère également les ressources rapidement en quittant la portée.

40voto

kent Points 2151

dans l'esprit du partage... voici une méthode que j'utilise fréquemment pour exécuter des scripts shell. vous pouvez ajouter un script à votre bundle de produit (dans la phase de copie de la construction) et puis faire en sorte que le script soit lu et exécuté au moment de l'exécution. note : ce code recherche le script dans le sous-chemin privateFrameworks. avertissement : cela pourrait être un risque de sécurité pour les produits déployés, mais pour notre développement interne, c'est un moyen facile de personnaliser des choses simples (comme l'hôte vers lequel rsync...) sans recompiler l'application, mais juste en modifiant le shell script dans le bundle.

//------------------------------------------------------
-(void) runScript:(NSString*)scriptName
{
    NSTask *task;
    task = [[NSTask alloc] init];
    [task setLaunchPath: @"/bin/sh"];

    NSArray *arguments;
    NSString* newpath = [NSString stringWithFormat:@"%@/%@",[[NSBundle mainBundle] privateFrameworksPath], scriptName];
    NSLog(@"shell script path: %@",newpath);
    arguments = [NSArray arrayWithObjects:newpath, nil];
    [task setArguments: arguments];

    NSPipe *pipe;
    pipe = [NSPipe pipe];
    [task setStandardOutput: pipe];

    NSFileHandle *file;
    file = [pipe fileHandleForReading];

    [task launch];

    NSData *data;
    data = [file readDataToEndOfFile];

    NSString *string;
    string = [[NSString alloc] initWithData: data encoding: NSUTF8StringEncoding];
    NSLog (@"script returned:\n%@", string);    
}
//------------------------------------------------------

Edit : Inclus un correctif pour le problème NSLog

Si vous utilisez NSTask pour exécuter un utilitaire de ligne de commande via bash, alors vous devez inclure cette ligne magique pour que NSLog fonctionne :

//The magic line that keeps your log where it belongs
[task setStandardInput:[NSPipe pipe]];

Dans le contexte :

NSPipe *pipe;
pipe = [NSPipe pipe];
[task setStandardOutput: pipe];
//The magic line that keeps your log where it belongs
[task setStandardInput:[NSPipe pipe]];

Une explication est ici : http://www.cocoadev.com/index.pl?NSTask

2 votes

Le lien d'explication est mort.

0 votes

Je veux exécuter la commande "system_profiler SPApplicationsDataType -xml" mais j'obtiens cette erreur "launch path not accessible".

15voto

Zach Hirsch Points 5040

fourchette , exec et attendre devrait fonctionner, si vous ne cherchez pas vraiment une méthode spécifique à Objective-C. fork crée une copie du programme en cours d'exécution, exec remplace le programme en cours d'exécution par un nouveau programme, et wait attend que le sous-processus se termine. Par exemple (sans aucune vérification d'erreur) :

#include <stdlib.h>
#include <unistd.h>

pid_t p = fork();
if (p == 0) {
    /* fork returns 0 in the child process. */
    execl("/other/program/to/run", "/other/program/to/run", "foo", NULL);
} else {
    /* fork returns the child's PID in the parent. */
    int status;
    wait(&status);
    /* The child has exited, and status contains the way it exited. */
}

/* The child has run and exited by the time execution gets to here. */

Il y a aussi système qui exécute la commande comme si vous l'aviez tapée depuis la ligne de commande du shell. C'est plus simple, mais vous avez moins de contrôle sur la situation.

Je suppose que vous travaillez sur une application Mac, donc les liens sont vers la documentation d'Apple pour ces fonctions, mais ils sont tous POSIX Vous devriez donc pouvoir les utiliser sur tout système compatible POSIX.

0 votes

Je sais que c'est une très vieille réponse mais je dois dire ceci : c'est une excellente façon d'utiliser trheads pour gérer l'exécution. le seul inconvénient est que cela crée une copie du programme entier. donc pour une application cacao j'irais avec @GordonWilson pour une approche plus agréable, et si je travaille sur une application en ligne de commande c'est la meilleure façon de le faire. merci (désolé mon mauvais anglais)

11voto

nes1983 Points 7046

Il y a aussi le bon vieux POSIX système ("echo -en ' \007 '") ;

6 votes

N'EXÉCUTEZ PAS CETTE COMMANDE. (Au cas où vous ne sauriez pas ce que fait cette commande)

4 votes

Je l'ai changé pour quelque chose de plus sûr (il émet un bip)

0 votes

Cela ne va-t-il pas provoquer une erreur dans la console ? Incorrect NSStringEncoding value 0x0000 detected. Assuming NSStringEncodingASCII. Will stop this compatibility mapping behavior in the near future.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X