Execute a terminal command from a Cocoa app

后端 未结 12 2131
既然无缘
既然无缘 2020-11-22 06:34

How can I execute a terminal command (like grep) from my Objective-C Cocoa application?

相关标签:
12条回答
  • 2020-11-22 06:59

    Or since Objective C is just C with some OO layer on top you can use the posix conterparts:

    int execl(const char *path, const char *arg0, ..., const char *argn, (char *)0);
    int execle(const char *path, const char *arg0, ..., const char *argn, (char *)0, char *const envp[]);
    int execlp(const char *file, const char *arg0, ..., const char *argn, (char *)0);
    int execlpe(const char *file, const char *arg0, ..., const char *argn, (char *)0, char *const envp[]);
    int execv(const char *path, char *const argv[]);
    int execve(const char *path, char *const argv[], char *const envp[]);
    int execvp(const char *file, char *const argv[]);
    int execvpe(const char *file, char *const argv[], char *const envp[]); 
    

    They are included from unistd.h header file.

    0 讨论(0)
  • 2020-11-22 07:02

    in the spirit of sharing... this is a method I use frequently to run shell scripts. you can add a script to your product bundle (in the copy phase of the build) and then have the script be read and run at runtime. note: this code looks for the script in the privateFrameworks sub-path. warning: this could be a security risk for deployed products, but for our in-house development it is an easy way to customize simple things (like which host to rsync to...) without re-compiling the application, but just editing the shell script in the 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: Included fix for NSLog problem

    If you are using NSTask to run a command-line utility via bash, then you need to include this magic line to keep NSLog working:

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

    In context:

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

    An explanation is here: http://www.cocoadev.com/index.pl?NSTask

    0 讨论(0)
  • 2020-11-22 07:03

    In addition to the several excellent answers above, I use the following code to process the output of the command in the background and avoid the blocking mechanism of [file readDataToEndOfFile].

    - (void)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];
    
        [self performSelectorInBackground:@selector(collectTaskOutput:) withObject:file];
    }
    
    - (void)collectTaskOutput:(NSFileHandle *)file
    {
        NSData      *data;
        do
        {
            data = [file availableData];
            NSLog(@"%@", [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] );
    
        } while ([data length] > 0); // [file availableData] Returns empty data when the pipe was closed
    
        // Task has stopped
        [file closeFile];
    }
    
    0 讨论(0)
  • 2020-11-22 07:03

    Custos Mortem said:

    I'm surprised no one really got into blocking/non-blocking call issues

    For blocking/non-blocking call issues regarding NSTask read below:

    asynctask.m -- sample code that shows how to implement asynchronous stdin, stdout & stderr streams for processing data with NSTask

    Source code of asynctask.m is available at GitHub.

    0 讨论(0)
  • 2020-11-22 07:05

    If the Terminal command requires Administrator Privilege (aka sudo), use AuthorizationExecuteWithPrivileges instead. The following will create a file named "com.stackoverflow.test" is the root directory "/System/Library/Caches".

    AuthorizationRef authorizationRef;
    FILE *pipe = NULL;
    OSStatus err = AuthorizationCreate(nil,
                                       kAuthorizationEmptyEnvironment,
                                       kAuthorizationFlagDefaults,
                                       &authorizationRef);
    
    char *command= "/usr/bin/touch";
    char *args[] = {"/System/Library/Caches/com.stackoverflow.test", nil};
    
    err = AuthorizationExecuteWithPrivileges(authorizationRef,
                                             command,
                                             kAuthorizationFlagDefaults,
                                             args,
                                             &pipe); 
    
    0 讨论(0)
  • 2020-11-22 07:06

    kent's article gave me a new idea. this runCommand method doesn't need a script file, just runs a command by a line:

    - (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;
    }
    

    You can use this method like this:

    NSString *output = runCommand(@"ps -A | grep mysql");
    
    0 讨论(0)
提交回复
热议问题