Comment puis-je exécuter une commande de terminal (comme grep
) depuis mon application Objective-C Cocoa ?
Merci beaucoup. Si je dois ajouter des options comme -e, dois-je les ajouter au tableau des arguments également ?
Comment puis-je exécuter une commande de terminal (comme grep
) depuis mon application Objective-C Cocoa ?
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
Merci beaucoup. Si je dois ajouter des options comme -e, dois-je les ajouter au tableau des arguments également ?
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");
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.
@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.
@Kenial : Oh, c'est une bien meilleure solution. Elle libère également les ressources rapidement en quittant la portée.
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
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.
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)
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 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.
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/bin
oùgrep
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.