เรียกใช้งานคำสั่งเทอร์มินัลจากแอป Cocoa


204

ฉันจะรันคำสั่งเทอร์มินัล (เหมือนgrep) จากแอปพลิเคชัน Objective-C Cocoa ของฉันได้อย่างไร


2
ฉันแค่บอกชัดเจน: ด้วย sandboxing คุณไม่สามารถเริ่มแอพที่ไม่ได้อยู่ใน sandbox ของคุณและพวกเขาจะต้องลงชื่อโดยคุณเพื่ออนุญาตสิ่งนี้
Daij-Djan

@ Daij-Djan ที่ไม่เป็นความจริงเลยอย่างน้อยก็ไม่ได้อยู่ใน macOS แอพ macOS แบบ sandbox สามารถเรียกใช้ไบนารี่ใด ๆ ในสถานที่ต่าง ๆ เช่น/usr/binที่ที่มีgrepชีวิต
jeff-h

ไม่ได้โปรดพิสูจน์ว่าฉันผิด;) บน ist nstask จะไม่สามารถทำงานอะไรที่ไม่ได้อยู่ในแซนด์บ็อกซ์ของคุณ
Daij-Djan

คำตอบ:


282

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


1
Yup, 'arguments = [NSArray arrayWithObjects: @ "- e", @ "foo", @ "bar.txt", ไม่มี];'
Gordon Wilson

14
คำตอบของคุณมีข้อผิดพลาดเล็กน้อย NSPipe มีบัฟเฟอร์ (ตั้งค่าที่ระดับ OS) ซึ่งถูกลบทิ้งเมื่ออ่าน หากบัฟเฟอร์เต็ม NSTask จะหยุดทำงานและแอปของคุณจะวางสายด้วยเช่นกันโดยไม่มีกำหนด จะไม่มีข้อความแสดงข้อผิดพลาดปรากฏขึ้น สิ่งนี้สามารถเกิดขึ้นได้หาก NSTask ส่งคืนข้อมูลจำนวนมาก NSMutableData *data = [NSMutableData dataWithCapacity:512];การแก้ปัญหาคือการใช้งาน จากนั้นwhile ([task isRunning]) { [data appendData:[file readDataToEndOfFile]]; }. และฉัน "เชื่อ" คุณควรมีอีกหนึ่ง[data appendData:[file readDataToEndOfFile]];หลังจากออกจาก while-loop
Dave Gallagher

2
ข้อผิดพลาดจะไม่เกิดขึ้นเว้นแต่คุณจะทำสิ่งนี้ (พวกเขาเพิ่งพิมพ์ในบันทึก): [งาน setStandardError: pipe];
Mike Sprague

1
สิ่งนี้สามารถอัปเดตด้วย ARC และตัวอักษรอาร์เรย์ Obj-C เช่นpastebin.com/sRvs3CqD
bames53

1
เป็นความคิดที่ดีที่จะแก้ไขข้อผิดพลาด task.standardError = pipe;
vqdave

43

บทความของ kent ทำให้ฉันมีความคิดใหม่ เมธอด 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");

1
ซึ่งจัดการกรณีส่วนใหญ่ได้ดี แต่ถ้าคุณเรียกใช้ในการวนซ้ำในที่สุดก็ยกข้อยกเว้นเนื่องจากการจัดการไฟล์เปิดมากเกินไป สามารถแก้ไขได้โดยการเพิ่ม: [file closeFile]; หลังจากอ่าน DataToEndOfFile
David Stein

@DavidStein: ฉันคิดว่าการใช้ autoreleasepool เพื่อห่อคำสั่ง runCommand ดูเหมือนว่าจะดีกว่า ที่จริงแล้วโค้ดข้างต้นไม่ได้พิจารณาว่าไม่ใช่ ARC เช่นกัน
Kenial

@Kenial: โอ้นั่นเป็นทางออกที่ดีกว่ามาก นอกจากนี้ยังเผยแพร่ทรัพยากรทันทีเมื่อออกจากขอบเขต
David Stein

/ bin / ps: ไม่อนุญาตให้ใช้งานฉันไม่ได้รับความสำเร็จนำไปสู่
Naman Vaishnav

40

ในจิตวิญญาณของการแบ่งปัน ... นี่เป็นวิธีที่ฉันใช้เป็นประจำเพื่อเรียกใช้เชลล์สคริปต์ คุณสามารถเพิ่มสคริปต์ลงในชุดผลิตภัณฑ์ของคุณ (ในขั้นตอนการคัดลอกของบิลด์) จากนั้นให้สคริปต์อ่านและรันในรันไทม์ หมายเหตุ: รหัสนี้จะค้นหาสคริปต์ในเส้นทางย่อย privateFrameworks คำเตือน: นี่อาจเป็นความเสี่ยงด้านความปลอดภัยสำหรับผลิตภัณฑ์ที่ปรับใช้ แต่สำหรับการพัฒนาภายใน บริษัท ของเรามันเป็นวิธีที่ง่ายในการปรับแต่งสิ่งต่าง ๆ (เช่นโฮสต์ที่ rsync ไปที่ ... ) โดยไม่ต้องรวบรวมแอปพลิเคชันใหม่ เชลล์สคริปต์ในบันเดิล

//------------------------------------------------------
-(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


2
ลิงก์คำอธิบายตายแล้ว
จอนนี่

ฉันต้องการเรียกใช้คำสั่งนี้ "system_profiler SPApplicationsDataType -xml" แต่ฉันได้รับข้อผิดพลาดนี้ "ไม่สามารถเปิดเส้นทางการเข้าถึง"
Vikas Bansal

25

นี่คือวิธีการทำใน Swift

การเปลี่ยนแปลงสำหรับ Swift 3.0:

  • NSPipe ถูกเปลี่ยนชื่อแล้ว Pipe

  • NSTask ถูกเปลี่ยนชื่อแล้ว Process


นี่คือคำตอบ Objective-C ของ inkit ข้างต้น เขาเขียนว่ามันเป็นหมวดหมู่บนNSString- สำหรับสวิฟท์มันจะกลายเป็นส่วนขยายของStringของ

ส่วนขยาย 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 ---"
        }
    }
}

การใช้งาน:

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แต่มันก็ควรจะเป็น

ดังนั้นตราบใดที่มันไม่ใช่ศูนย์ผลที่ได้ก็จะกลายเป็นสวิฟท์ Stringและกลับมา

หากด้วยเหตุผลบางอย่างที่ไม่NSStringสามารถเริ่มต้นได้จากข้อมูลไฟล์ฟังก์ชั่นจะส่งกลับข้อผิดพลาด ฟังก์ชั่นนี้อาจถูกเขียนขึ้นเพื่อส่งกลับทางเลือกString?แต่ก็น่าใช้และไม่เหมาะสำหรับวัตถุประสงค์ที่เป็นประโยชน์เพราะมันไม่น่าจะเกิดขึ้นได้


1
เป็นวิธีที่ดีและสง่างามจริงๆ! คำตอบนี้ควรมีผู้โหวตมากขึ้น
XueYu

หากคุณไม่ต้องการเอาต์พุต เพิ่มอาร์กิวเมนต์ @discardableResult ด้านหน้าหรือด้านบนของเมธอด runCommand วิธีนี้จะทำให้คุณสามารถเรียกใช้เมธอดได้โดยไม่ต้องใส่ลงในตัวแปร
Lloyd Keijzer

ให้ผลลัพธ์ = สตริง (ไบต์: fileHandle.readDataToEndOfFile (), การเข้ารหัส: String.Encoding.utf8) ก็โอเค
cleexiang

18

Objective-C (ดูด้านล่างสำหรับ Swift)

ทำความสะอาดรหัสในคำตอบด้านบนเพื่อให้อ่านง่ายขึ้นซ้ำซ้อนน้อยลงเพิ่มประโยชน์ของวิธีการหนึ่งบรรทัดและสร้างเป็นหมวดหมู่ 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];

หวังว่ามันจะมีประโยชน์กับคุณอย่างที่มันจะเป็นในอนาคตฉัน (สวัสดีคุณ!)


สวิฟต์ 4

นี่คือสวิฟท์ยกตัวอย่างเช่นการใช้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()

2
แน่นอนรหัสของคุณมีประโยชน์มากสำหรับฉัน! ฉันเปลี่ยนเป็น Swift และโพสต์เป็นคำตอบอื่นด้านล่าง
ElmerCat

14

fork , execและwaitควรทำงานถ้าคุณไม่ได้มองหา Objective-C 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. */

นอกจากนี้ยังมีระบบซึ่งรันคำสั่งราวกับว่าคุณพิมพ์จากบรรทัดคำสั่งของเชลล์ ง่ายกว่า แต่คุณสามารถควบคุมสถานการณ์ได้น้อยลง

ฉันสมมติว่าคุณกำลังทำงานกับแอปพลิเคชั่น Mac ดังนั้นลิงก์ที่เชื่อมโยงไปยังเอกสารประกอบของ Apple สำหรับฟังก์ชั่นเหล่านี้ แต่ทั้งหมดPOSIXนี้คุณควรใช้มันกับระบบที่สอดคล้องกับ POSIX


ฉันรู้ว่านี่เป็นคำตอบที่เก่ามาก แต่ฉันต้องพูดแบบนี้: นี่เป็นวิธีที่ยอดเยี่ยมในการใช้ trheads เพื่อจัดการกับข้อยกเว้น ข้อเสียเพียงอย่างเดียวคือมันสร้างสำเนาของโปรแกรมทั้งหมด ดังนั้นสำหรับแอปพลิเคชันโกโก้ฉันจะไปกับ @GordonWilson สำหรับ aproach ที่ดีกว่าและหากฉันกำลังทำงานกับแอปพลิเคชันบรรทัดคำสั่งนี่เป็นวิธีที่ดีที่สุดที่จะทำ ขอบคุณ (ขออภัยภาษาอังกฤษของฉันไม่ดี)
Nicos Karalis

11

นอกจากนี้ยังมีระบบ POSIX แบบเก่าที่ดี("echo -en '\ 007'");


6
อย่าเรียกใช้คำสั่งนี้ (ในกรณีที่คุณไม่ทราบว่าคำสั่งนี้ทำอะไร)
justin

4
เปลี่ยนไปบางสิ่งบางอย่างเล็กน้อยปลอดภัย ... (มันดังขึ้น)
nes1983

สิ่งนี้จะไม่เกิดข้อผิดพลาดในคอนโซลหรือไม่ Incorrect NSStringEncoding value 0x0000 detected. Assuming NSStringEncodingASCII. Will stop this compatibility mapping behavior in the near future.
cwd

1
อืมมม บางทีคุณอาจต้องหลบแบ็กสแลชสองครั้ง
nes1983

เพียงแค่เรียกใช้ / usr / bin / echo หรืออะไรบางอย่าง rm -rf มีความรุนแรงและ unicode ในคอนโซลยังคงแย่อยู่ :)
Maxim Veksler

8

ฉันเขียนฟังก์ชัน "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มายังคงเป็นเรื่องยุ่งเหยิงสำหรับฉัน .. และด้วยความเชื่อเล็กน้อยในความสามารถในการแก้ไขข้อบกพร่องขั้นต้นของฉัน - สาขามะกอกเดียวที่ฉันเสนอคือคำตอบของ @ inket รุ่น rezhuzhed ที่แทบไม่มีกระดูกสำหรับเพื่อนของฉัน คนเจ้าระเบียบ / การใช้คำฟุ่มเฟื่อย ...

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

1
outP ไม่ได้ถูกกำหนดในข้อผิดพลาดใด ๆ chars_read มีขนาดเล็กเกินไปสำหรับค่าส่งคืนของ fread () บนสถาปัตยกรรมใด ๆ ที่ sizeof (ssize_t)! = sizeof (int) ถ้าเราต้องการเอาต์พุตมากกว่าไบต์ BUFSIZ? เกิดอะไรขึ้นถ้าผลลัพธ์ไม่ใช่ UTF-8 ถ้า pclose () ส่งคืนข้อผิดพลาดล่ะ เราจะรายงานข้อผิดพลาดของ fread ได้อย่างไร?
ObjectiveC-oder

@ ObjectiveC-oder D'oh - ฉันไม่ได้ กรุณาบอกฉัน (เช่น .. แก้ไขไป)!
Alex Grey

3

Custos Mortem กล่าวว่า:

ฉันประหลาดใจที่ไม่มีใครได้รับจริงๆเกี่ยวกับปัญหาการโทร / ไม่บล็อก

สำหรับการบล็อกปัญหาการโทร / ไม่บล็อคเกี่ยวกับการNSTaskอ่านด้านล่าง:

asynctask.m - โค้ดตัวอย่างที่แสดงวิธีใช้ stdin แบบอะซิงโครนัส, stdout & stderr สำหรับการประมวลผลข้อมูลด้วย NSTask

Source code ของ asynctask.m สามารถใช้ได้ที่GitHub


ดูการมีส่วนร่วมของฉันสำหรับรุ่นที่ไม่มีการปิดกั้น
Guruniverse

3

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

สำหรับฉันบรรทัดที่สร้างความแตกต่างทั้งหมดคือ [self performSelectorInBackground: @selector (collectTaskOutput :) withObject: file];
neowinston

2

หรือเนื่องจาก Objective C เป็นเพียง C ที่มีเลเยอร์ OO อยู่ด้านบนคุณสามารถใช้ 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


2

หากคำสั่ง Terminal ต้องการสิทธิ์ของผู้ดูแลระบบ (aka 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); 

4
สิ่งนี้ได้รับการคัดค้านอย่างเป็นทางการตั้งแต่ OS X 10.7
Sam Washburn

2
.. แต่มันยังคงทำงานโดยไม่คำนึงว่าเป็นวิธีเดียวที่จะทำเช่นนี้และผู้ติดตั้งจำนวนมากเชื่อมั่นในสิ่งนั้น
Thomas Tempelmann
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.