programa para mac linea ejecutar desde comandos comando aplicaciones abrir objective-c cocoa macos

objective-c - para - manual de comandos de linux ubuntu pdf



Ejecutar un comando de terminal desde una aplicaciĆ³n Cocoa (11)

¿Cómo puedo ejecutar un comando de terminal (como grep ) desde mi aplicación Objective-C Cocoa?


He aquí cómo hacerlo en Swift.

Cambios para Swift 3.0:

  • NSPipe ha cambiado su nombre a Pipe

  • NSTask ha sido renombrado como Process

Esto se basa en la respuesta Objective-C anterior de inkit. Lo escribió como una categoría en NSString - Para Swift, se convierte en una extensión de String .

extensión String.runAsCommand () -> String

extension String { func runAsCommand() -> String { let pipe = Pipe() let task = Process() task.launchPath = "/bin/sh" task.arguments = ["-c", String(format:"%@", self)] task.standardOutput = pipe let file = pipe.fileHandleForReading task.launch() if let result = NSString(data: file.readDataToEndOfFile(), encoding: String.Encoding.utf8.rawValue) { return result as String } else { return "--- Error running command - Unable to initialize string from file data ---" } } }

Uso:

let input = "echo hello" let output = input.runAsCommand() print(output) // prints "hello"

o solo:

print("echo hello".runAsCommand()) // prints "hello"

Ejemplo:

@IBAction func toggleFinderShowAllFiles(_ sender: AnyObject) { var newSetting = "" let readDefaultsCommand = "defaults read com.apple.finder AppleShowAllFiles" let oldSetting = readDefaultsCommand.runAsCommand() // Note: the Command results are terminated with a newline character if (oldSetting == "0/n") { newSetting = "1" } else { newSetting = "0" } let writeDefaultsCommand = "defaults write com.apple.finder AppleShowAllFiles /(newSetting) ; killall Finder" _ = writeDefaultsCommand.runAsCommand() }

Tenga en cuenta que el resultado del Process tal como se lee en la Pipe es un objeto NSString . Puede ser una cadena de error y también puede ser una cadena vacía, pero siempre debe ser una NSString .

Por lo tanto, siempre que no sea nulo, el resultado se puede convertir como una String Swift y se devuelve.

Si por algún motivo no se puede inicializar NSString a partir de los datos del archivo, la función devuelve un mensaje de error. ¿La función podría haberse escrito para devolver una String? opcional String? , pero eso sería difícil de usar y no serviría para un propósito útil porque es muy poco probable que esto ocurra.


Objective-C (ver abajo para Swift)

Limpié el código en la respuesta superior para hacerlo más legible, menos redundante, agregó los beneficios del método de una línea y se convirtió en una categoría NSString

@interface NSString (ShellExecution) - (NSString*)runAsCommand; @end

Implementación:

@implementation NSString (ShellExecution) - (NSString*)runAsCommand { NSPipe* pipe = [NSPipe pipe]; NSTask* task = [[NSTask alloc] init]; [task setLaunchPath: @"/bin/sh"]; [task setArguments:@[@"-c", [NSString stringWithFormat:@"%@", self]]]; [task setStandardOutput:pipe]; NSFileHandle* file = [pipe fileHandleForReading]; [task launch]; return [[NSString alloc] initWithData:[file readDataToEndOfFile] encoding:NSUTF8StringEncoding]; } @end

Uso:

NSString* output = [@"echo hello" runAsCommand];

Y si tienes problemas con la codificación de salida:

// Had problems with `lsof` output and Japanese-named files, this fixed it NSString* output = [@"export LANG=en_US.UTF-8;echo hello" runAsCommand];

Espero que sea tan útil para ti como lo será para mi futuro. (¡Hola!)

Swift 4

Aquí hay un ejemplo de Swift que utiliza Pipe , Process y String

extension String { func run() -> String? { let pipe = Pipe() let process = Process() process.launchPath = "/bin/sh" process.arguments = ["-c", self] process.standardOutput = pipe let fileHandle = pipe.fileHandleForReading process.launch() return String(data: fileHandle.readDataToEndOfFile(), encoding: .utf8) } }

Uso:

let output = "echo hello".run()


Custos Mortem dijo:

Me sorprende que nadie se haya metido en problemas de llamadas de bloqueo / no bloqueo.

Para bloquear / no bloquear los problemas de llamadas relacionados con NSTask lea a continuación:

asynctask.m: código de ejemplo que muestra cómo implementar streams asíncronos, stdout y stderr streams para procesar datos con NSTask

El código fuente de asynctask.m está disponible en GitHub .


El artículo de Kent me dio una nueva idea. este método runCommand no necesita un archivo de script, solo ejecuta un comando por una línea:

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

Puedes usar este método así:

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


En el espíritu de compartir ... este es un método que utilizo con frecuencia para ejecutar scripts de shell. puede agregar un script a su paquete de producto (en la fase de copia de la compilación) y luego hacer que el script se lea y ejecute en tiempo de ejecución. nota: este código busca el script en la ruta secundaria privateFrameworks. advertencia: esto podría ser un riesgo de seguridad para los productos implementados, pero para nuestro desarrollo interno es una manera fácil de personalizar cosas simples (como el host al que rsync ...) sin volver a compilar la aplicación, pero simplemente editando el shell script en el paquete.

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

Edición: corrección incluida para el problema de NSLog

Si está utilizando NSTask para ejecutar una utilidad de línea de comandos a través de bash, debe incluir esta línea mágica para mantener el NSLog en funcionamiento:

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

En contexto:

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

Una explicación está aquí: http://www.cocoadev.com/index.pl?NSTask


Escribí esta función "C", porque NSTask es desagradable ..

NSString * runCommand(NSString* c) { NSString* outP; FILE *read_fp; char buffer[BUFSIZ + 1]; int chars_read; memset(buffer, ''/0'', sizeof(buffer)); read_fp = popen(c.UTF8String, "r"); if (read_fp != NULL) { chars_read = fread(buffer, sizeof(char), BUFSIZ, read_fp); if (chars_read > 0) outP = $UTF8(buffer); pclose(read_fp); } return outP; } NSLog(@"%@", runCommand(@"ls -la /")); total 16751 drwxrwxr-x+ 60 root wheel 2108 May 24 15:19 . drwxrwxr-x+ 60 root wheel 2108 May 24 15:19 .. …

Ah, y por ser completo / inequívoco ...

#define $UTF8(A) ((NSString*)[NSS stringWithUTF8String:A])

Años más tarde, C sigue siendo un lío desconcertante, para mí ... y con poca fe en mi capacidad para corregir mis deficiencias anteriores. compañeros puristas / aborrecedores de la verbosidad ...

id _system(id cmd) { return !cmd ? nil : ({ NSPipe* pipe; NSTask * task; [task = NSTask.new setValuesForKeysWithDictionary: @{ @"launchPath" : @"/bin/sh", @"arguments" : @[@"-c", cmd], @"standardOutput" : pipe = NSPipe.pipe}]; [task launch]; [NSString.alloc initWithData: pipe.fileHandleForReading.readDataToEndOfFile encoding:NSUTF8StringEncoding]; }); }


O como Objective C es solo C con alguna capa OO en la parte superior, puedes usar las contrapartes de posix:

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[]);

Se incluyen desde el archivo de cabecera unistd.h.


Puedes usar NSTask . Aquí hay un ejemplo que ejecutaría '' /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 y NSFileHandle se utilizan para redirigir la salida estándar de la tarea.

Para obtener información más detallada sobre cómo interactuar con el sistema operativo desde su aplicación Objective-C, puede ver este documento en el Centro de desarrollo de Apple: Interactuar con el sistema operativo .

Edición: corrección incluida para el problema de NSLog

Si está utilizando NSTask para ejecutar una utilidad de línea de comandos a través de bash, debe incluir esta línea mágica para mantener el NSLog en funcionamiento:

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

Una explicación está aquí: https://web.archive.org/web/20141121094204/https://cocoadev.com/HowToPipeCommandsWithNSTask


Si el comando del terminal requiere privilegios de administrador (también conocido como sudo ), use AuthorizationExecuteWithPrivileges lugar. Lo siguiente creará un archivo llamado "com..test" es el directorio raíz "/ 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..test", nil}; err = AuthorizationExecuteWithPrivileges(authorizationRef, command, kAuthorizationFlagDefaults, args, &pipe);


También hay un buen system POSIX antiguo ("echo -en ''/ 007''");


fork , exec , y wait deberían funcionar, si realmente no estás buscando una forma específica de Objective-C. fork crea una copia del programa que se está ejecutando actualmente, exec reemplaza el programa que se está ejecutando actualmente con uno nuevo y espera a que salga el subproceso. Por ejemplo (sin ninguna comprobación de errores):

#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. */

También hay un system que ejecuta el comando como si lo hubiera escrito desde la línea de comandos del shell. Es más sencillo, pero tienes menos control sobre la situación.

Supongo que está trabajando en una aplicación Mac, por lo que los enlaces son a la documentación de Apple para estas funciones, pero todos son POSIX , por lo que debería usarlos en cualquier sistema compatible con POSIX.