grep
Objective-C Cocoa 애플리케이션에서 터미널 명령 (예 :)을 어떻게 실행할 수 있습니까?
/usr/bin
곳에 grep
살고있다.
grep
Objective-C Cocoa 애플리케이션에서 터미널 명령 (예 :)을 어떻게 실행할 수 있습니까?
/usr/bin
곳에 grep
살고있다.
답변:
사용할 수 있습니다 NSTask
. 다음은 ' /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
그리고 NSFileHandle
작업의 표준 출력을 리디렉션하는 데 사용됩니다.
Objective-C 응용 프로그램 내에서 운영 체제와의 상호 작용에 대한 자세한 내용은 Apple 개발 센터 : 운영 체제와 상호 작용 에서이 문서를 볼 수 있습니다 .
편집 : NSLog 문제에 대한 수정 포함
NSTask를 사용하여 bash를 통해 명령 행 유틸리티를 실행하는 경우 NSLog가 계속 작동하도록하려면이 매직 라인을 포함시켜야합니다.
//The magic line that keeps your log where it belongs
task.standardOutput = pipe;
설명은 다음과 같습니다. https://web.archive.org/web/20141121094204/https://cocoadev.com/HowToPipeCommandsWithNSTask
NSMutableData *data = [NSMutableData dataWithCapacity:512];
입니다. 그런 다음, while ([task isRunning]) { [data appendData:[file readDataToEndOfFile]]; }
. 그리고 나는 " [data appendData:[file readDataToEndOfFile]];
루프 "하고 당신은 while 루프 종료 후 하나 더 가져야합니다 .
task.standardError = pipe;
켄트의 기사는 새로운 아이디어를 주었다. 이 runCommand 메소드는 스크립트 파일이 필요하지 않으며 명령 행을 한 줄씩 실행합니다.
- (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;
}
이 방법을 다음과 같이 사용할 수 있습니다.
NSString *output = runCommand(@"ps -A | grep mysql");
공유의 정신에서 ... 이것은 쉘 스크립트를 실행하기 위해 자주 사용하는 방법입니다. 빌드의 복사 단계에서 제품 번들에 스크립트를 추가 한 후 런타임에 스크립트를 읽고 실행할 수 있습니다. 참고 :이 코드는 privateFrameworks 하위 경로에서 스크립트를 찾습니다. 경고 : 배포 된 제품의 경우 보안 위험이 발생할 수 있지만 자체 개발의 경우 응용 프로그램을 다시 컴파일하지 않고 간단한 작업 (예 : rsync to host ... 등)을 사용자 지정하는 쉬운 방법입니다. 번들의 쉘 스크립트.
//------------------------------------------------------
-(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);
}
//------------------------------------------------------
편집 : NSLog 문제에 대한 수정 포함
NSTask를 사용하여 bash를 통해 명령 행 유틸리티를 실행하는 경우 NSLog가 계속 작동하도록하려면이 매직 라인을 포함시켜야합니다.
//The magic line that keeps your log where it belongs
[task setStandardInput:[NSPipe pipe]];
문맥:
NSPipe *pipe;
pipe = [NSPipe pipe];
[task setStandardOutput: pipe];
//The magic line that keeps your log where it belongs
[task setStandardInput:[NSPipe pipe]];
설명은 여기에 있습니다 : http://www.cocoadev.com/index.pl?NSTask
스위프트 3.0의 변경 사항 :
NSPipe
이름이 변경되었습니다Pipe
NSTask
이름이 변경되었습니다Process
이것은 위의 inkit의 Objective-C 답변을 기반으로합니다. 그는로 쓴 카테고리 에 NSString
- 스위프트를 들어,이된다 확장 의 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"
또는 그냥 :
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()
}
메모 Process
(가)에서 읽을 결과를 Pipe
입니다 NSString
객체입니다. 오류 문자열 일 수도 있고 빈 문자열 일 수도 있지만 항상이어야합니다 NSString
.
따라서 0이 아닌 한 결과는 스위프트로 캐스팅되어 String
반환 될 수 있습니다 .
어떤 이유로 NSString
파일 데이터에서 전혀 초기화 할 수 없는 경우 , 함수는 오류 메시지를 리턴합니다. 이 함수는 optional을 반환하도록 작성되었을 수 String?
있지만 사용하기가 어려우며 이것이 발생할 가능성이 적기 때문에 유용한 목적으로 사용되지 않습니다.
가장 읽기 쉬운 코드를 정리하여 읽기 쉽고 중복성을 줄이고 한 줄 방법 의 이점을 추가하고 NSString 범주로 만들었습니다.
@interface NSString (ShellExecution)
- (NSString*)runAsCommand;
@end
이행:
@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
용법:
NSString* output = [@"echo hello" runAsCommand];
그리고 경우에 당신은 출력 인코딩에 문제가있어 :
// Had problems with `lsof` output and Japanese-named files, this fixed it
NSString* output = [@"export LANG=en_US.UTF-8;echo hello" runAsCommand];
그것이 미래에 나에게 도움이 되길 바랍니다. (안녕하세요!)
다음의 스위프트 예를 만드는 사용이다 Pipe
, Process
및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)
}
}
용법:
let output = "echo hello".run()
Objective-C 고유의 방법을 찾지 않는다면 fork , exec 및 wait 가 작동해야합니다. fork
현재 실행중인 프로그램의 사본을 작성 exec
하고 현재 실행중인 프로그램을 새 프로그램으로 바꾸고 wait
서브 프로세스가 종료 될 때까지 기다립니다. 예를 들어 (오류 검사없이) :
#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. */
쉘의 명령 행에서 입력 한 것처럼 명령을 실행하는 system 도 있습니다 . 더 간단하지만 상황에 대한 통제력이 떨어집니다.
Mac 응용 프로그램에서 작업하고 있다고 가정하므로 이러한 기능에 대한 Apple 설명서의 링크는 모두 있지만 POSIX
모든 POSIX 호환 시스템에서 사용해야합니다.
오래된 POSIX 시스템도 있습니다 ( "echo -en '\ 007'").
Incorrect NSStringEncoding value 0x0000 detected. Assuming NSStringEncodingASCII. Will stop this compatibility mapping behavior in the near future.
이 "C"함수를 작성했습니다 NSTask
.
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 ..
…
아, 그리고 완전하고 모호하지 않게하기 위해…
#define $UTF8(A) ((NSString*)[NSS stringWithUTF8String:A])
몇 년 후, C
나에게 당황 혼란은 아직 .. 위 내 총 단점을 해결하기 위해 내 능력에 대한 믿음이 적은와 - 유일한 올리브 브랜치 I의 서비스를입니다 @ inket의 대답의 rezhuzhed 버전 뼈의 barest 내 동료를 위해는, 순수 주의자 / 상세한 증오 ...
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은 다음과 같이 말했습니다.
아무도 통화 차단 / 비 차단 통화 문제에 빠지지 않은 것에 놀랐습니다.
NSTask
아래 읽기 관련 차단 / 비 차단 통화 문제 :
asynctask.m-NSTask를 사용하여 데이터를 처리하기 위해 비동기 stdin, stdout 및 stderr 스트림을 구현하는 방법을 보여주는 샘플 코드
asynctask.m의 소스 코드는 GitHub 에서 제공됩니다 .
위의 몇 가지 훌륭한 답변 외에도 다음 코드를 사용하여 백그라운드에서 명령의 출력을 처리하고의 차단 메커니즘을 피하십시오 [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];
}
또는 Objective C는 OO 레이어가있는 C 일 뿐이므로 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[]);
그것들은 unistd.h 헤더 파일에 포함되어 있습니다.
터미널 명령에 관리자 권한 (일명 sudo
)이 필요한 경우 AuthorizationExecuteWithPrivileges
대신 사용하십시오. 다음은 "com.stackoverflow.test"라는 파일을 루트 디렉토리 "/ 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);