grep
Objective-C Cocoa uygulamamdan nasıl bir terminal komutu (gibi ) yürütebilirim ?
/usr/bin
nerede grep
hayatlarında.
grep
Objective-C Cocoa uygulamamdan nasıl bir terminal komutu (gibi ) yürütebilirim ?
/usr/bin
nerede grep
hayatlarında.
Yanıtlar:
Kullanabilirsiniz NSTask
. İşte çalışacak bir örnek ' /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
ve NSFileHandle
görevin standart çıktısını yönlendirmek için kullanılır.
Objective-C uygulamanızdan işletim sistemi ile etkileşim hakkında daha ayrıntılı bilgi için, Apple'ın Geliştirme Merkezi: İşletim Sistemi ile Etkileşim hakkında bu belgeyi görebilirsiniz. .
Düzenleme: NSLog sorunu için dahil düzeltme
Bash aracılığıyla bir komut satırı yardımcı programını çalıştırmak için NSTask kullanıyorsanız, NSLog'un çalışmasını sağlamak için bu sihirli satırı eklemeniz gerekir:
//The magic line that keeps your log where it belongs
task.standardOutput = pipe;
Bir açıklama burada: https://web.archive.org/web/20141121094204/https://cocoadev.com/HowToPipeCommandsWithNSTask
NSMutableData *data = [NSMutableData dataWithCapacity:512];
. Sonra while ([task isRunning]) { [data appendData:[file readDataToEndOfFile]]; }
. Ve [data appendData:[file readDataToEndOfFile]];
while-loop çıktıktan sonra bir tane daha olması gerektiğine "inanıyorum" .
task.standardError = pipe;
kent'in makalesi bana yeni bir fikir verdi. bu runCommand yönteminin bir komut dosyasına ihtiyacı yoktur, yalnızca bir komut satırı tarafından çalıştırılır:
- (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;
}
Bu yöntemi şu şekilde kullanabilirsiniz:
NSString *output = runCommand(@"ps -A | grep mysql");
paylaşma ruhu içinde ... Bu, kabuk komut dosyalarını çalıştırmak için sık kullandığım bir yöntem. ürün paketinize (derlemenin kopya aşamasında) bir komut dosyası ekleyebilir ve komut dosyasının çalışma zamanında okunmasını ve çalışmasını sağlayabilirsiniz. not: bu kod script'i privateFrameworks alt yolunda arar. Uyarı: Bu, dağıtılan ürünler için bir güvenlik riski olabilir, ancak şirket içi geliştirmemiz için, uygulamayı yeniden derlemeden basit şeyleri (rsync'in hangi ... gibi) özelleştirmenin kolay bir yoludur, ancak kabuk betiği.
//------------------------------------------------------
-(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);
}
//------------------------------------------------------
Düzenleme: NSLog sorunu için dahil düzeltme
Bash aracılığıyla bir komut satırı yardımcı programını çalıştırmak için NSTask kullanıyorsanız, NSLog'un çalışmasını sağlamak için bu sihirli satırı eklemeniz gerekir:
//The magic line that keeps your log where it belongs
[task setStandardInput:[NSPipe pipe]];
Bağlamda:
NSPipe *pipe;
pipe = [NSPipe pipe];
[task setStandardOutput: pipe];
//The magic line that keeps your log where it belongs
[task setStandardInput:[NSPipe pipe]];
Bir açıklama burada: http://www.cocoadev.com/index.pl?NSTask
Swift 3.0 için değişiklikler:
NSPipe
yeniden adlandırıldıPipe
NSTask
yeniden adlandırıldıProcess
Bu, yukarıdaki inkit'in Objective-C cevabına dayanmaktadır. O bir şekilde yazdım kategorisinde üzerinde NSString
- Swift için, bir haline uzatma ait 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 ---"
}
}
}
let input = "echo hello"
let output = input.runAsCommand()
print(output) // prints "hello"
ya da sadece:
print("echo hello".runAsCommand()) // prints "hello"
@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()
}
Not Process
okunan sonuç Pipe
bir bir NSString
nesne. Bir hata dizesi olabilir ve boş bir dize olabilir, ancak her zaman bir olmalıdır NSString
.
Yani, sıfır olmadığı sürece sonuç bir Swift olarak yayınlanabilir String
ve geri dönebilir.
Herhangi bir nedenle NSString
dosya verilerinden hiçbir değer başlatılamazsa, işlev bir hata mesajı döndürür. İşlev isteğe bağlı olarak döndürmek için yazılmış olabilir String?
, ancak kullanımı garip olurdu ve yararlı bir amaca hizmet etmeyecektir, çünkü bunun gerçekleşmesi pek olası değildir.
Daha okunabilir, daha az yedekli olmak için üst yanıttaki kodu temizledik , tek satırlı yöntemin avantajlarını ekledik ve bir NSString kategorisine dönüştürdüm
@interface NSString (ShellExecution)
- (NSString*)runAsCommand;
@end
Uygulama:
@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
Kullanımı:
NSString* output = [@"echo hello" runAsCommand];
Ve eğer çıkış kodlama ile ilgili sorunlar yaşıyorsanız:
// Had problems with `lsof` output and Japanese-named files, this fixed it
NSString* output = [@"export LANG=en_US.UTF-8;echo hello" runAsCommand];
Umarım gelecekteki bana olacak kadar faydalıdır. (Selam, sen!)
İşte bir Swift örneği verme kullanımı var Pipe
, Process
veString
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)
}
}
Kullanımı:
let output = "echo hello".run()
Gerçekten Objective-C'ye özgü bir yol arıyorsanız , fork , exec ve wait çalışmalıdır. fork
çalışmakta olan programın bir kopyasını oluşturur, çalışmakta olan programı exec
yenisiyle değiştirir wait
ve alt işlemin çıkmasını bekler. Örneğin (hata kontrolü olmadan):
#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. */
Komutu kabuğun komut satırından yazmış gibi çalıştıran bir sistem de vardır . Daha basit, ancak durum üzerinde daha az kontrole sahipsiniz.
Bir Mac uygulaması üzerinde çalıştığınızı varsayıyorum, bu yüzden bağlantılar bu işlevler için Apple'ın belgelerine aittir, ancak hepsi bu POSIX
yüzden bunları herhangi bir POSIX uyumlu sistemde kullanmalısınız.
Ayrıca iyi eski POSIX sistemi ("echo -en '\ 007'");
Incorrect NSStringEncoding value 0x0000 detected. Assuming NSStringEncodingASCII. Will stop this compatibility mapping behavior in the near future.
Bu "C" fonksiyonunu yazdım, çünkü NSTask
iğrenç ..
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 ..
…
oh, tam / açık olmak adına…
#define $UTF8(A) ((NSString*)[NSS stringWithUTF8String:A])
Yıllar sonra, C
hala benim için şaşırtıcı bir karmaşa var ... ve yukarıdaki brüt eksikliklerimi düzeltme yeteneğime az bir inançla - sunduğum tek zeytin dalı, @ inket'in kemikler için en cevabı olan cevabının rezhuzhed versiyonu, dostum için puristler / ayrıntılar ...
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]; });
}
Custos Mortem şunları söyledi:
Kimsenin gerçekten engelleme / engelleme yapma sorunlarına girmediğine şaşırdım
NSTask
Aşağıdakileri okumakla ilgili engelleme / engellemeyen çağrı sorunları için :
asynctask.m - NSTask ile veri işlemek için eşzamansız stdin, stdout ve stderr akışlarının nasıl uygulanacağını gösteren örnek kod
Asynctask.m kaynak kodu GitHub'da bulunabilir .
Yukarıdaki birkaç mükemmel yanıta ek olarak, arka planda komut çıktısını işlemek ve engelleme mekanizmasını önlemek için aşağıdaki kodu kullanın [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];
}
Veya Objective C sadece üstte bazı OO katmanı olan C olduğundan, posix karşılaştırma parçalarını kullanabilirsiniz:
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[]);
Bunlar unistd.h başlık dosyasından eklenir.
Terminal komutu Yönetici Ayrıcalığı (aka sudo
) gerektiriyorsa , AuthorizationExecuteWithPrivileges
bunun yerine kullanın. Aşağıdaki "com.stackoverflow.test" adlı bir dosya oluşturur "/ System / Library / Caches" kök dizinidir.
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);