objective c - একটি কোকো অ্যাপ্লিকেশন থেকে একটি টার্মিনাল কমান্ড সঞ্চালন




objective-c cocoa (8)

আমি কিভাবে আমার উদ্দেশ্য-সি কোকো অ্যাপ্লিকেশন থেকে একটি টার্মিনাল কমান্ড ( grep মত) চালাতে পারি?


সুইফ্ট এ কিভাবে করবেন তা এখানে

সুইফট 3.0 এর জন্য পরিবর্তনগুলি:

  • NSPipe নামকরণ করা হয়েছে Pipe

  • NSTask নামকরণ করা হয়েছে নাম

এটি উপরের ইঙ্কিটের উদ্দেশ্য-সি উত্তরের উপর ভিত্তি করে তৈরি। তিনি NSString - Swift এর জন্য একটি বিভাগ হিসাবে এটি লিখেছেন, এটি String এক্সটেনশন হয়ে ওঠে।

এক্সটেনশন String.runAsCommand () -> স্ট্রিং

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

}

Pipe থেকে পড়ুন হিসাবে Process ফলাফল নোট করুন একটি NSString বস্তু। এটি একটি ত্রুটি স্ট্রিং হতে পারে এবং এটি একটি খালি NSString হতে পারে, তবে এটি সর্বদা একটি NSString হওয়া উচিত।

সুতরাং, যতক্ষণ না এটি nil হয়, ফলাফল একটি সুইফট String হিসাবে নিক্ষিপ্ত এবং ফিরে আসতে পারে।

কোনও কারণে যদি কোনও NSString ফাইল ফাইল থেকে শুরু করা যেতে পারে তবে ফাংশনটি একটি ত্রুটি বার্তা প্রদান করে। ফাংশন একটি ঐচ্ছিক String? ফেরত লেখা হয়েছে 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];

এটা ভবিষ্যতে হতে হবে হিসাবে এটি আপনার জন্য দরকারী হিসাবে আশা করি। (ওহে তুমি!)

সুইফ্ট 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()

আপনি NSTask ব্যবহার করতে পারেন। এখানে একটি উদাহরণ যা ' /usr/bin/grep foo bar.txt ' /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 স্ট্যান্ডার্ড আউটপুট পুনঃনির্দেশিত করতে ব্যবহৃত হয়।

আপনার উদ্দেশ্য-সি অ্যাপ্লিকেশন থেকে অপারেটিং সিস্টেমের সাথে ইন্টারঅ্যাক্ট করার আরও বিস্তারিত তথ্যের জন্য, আপনি অ্যাপল এর ডেভেলপমেন্ট সেন্টারে এই দস্তাবেজটি দেখতে পারেন: অপারেটিং সিস্টেমের সাথে ইন্টারঅ্যাক্টিং

সম্পাদনা: NSLog সমস্যা জন্য অন্তর্ভুক্ত ফিক্স

আপনি যদি ব্যাশের মাধ্যমে একটি কমান্ড-লাইন ইউটিলিটি চালানোর জন্য NSTask ব্যবহার করেন তবে NSLog কাজ করার জন্য আপনাকে এই জাদু লাইনটি অন্তর্ভুক্ত করতে হবে:

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

একটি ব্যাখ্যা এখানে রয়েছে: https://web.archive.org/web/20141121094204/https://cocoadev.com/HowToPipeCommandsWithNSTask


আমি এই "সি" ফাংশনটি লিখেছি, কারণ 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 এখনও আমার জন্য একটি বিদ্বেষপূর্ণ জগাখিচুড়ি, এবং উপরে আমার গুরুতর ত্রুটি সংশোধন করার আমার সামান্য বিশ্বাসের সাথে - আমি যে একমাত্র জলপাই শাখা প্রস্তাব করি তা হল @ ইনকেটের উত্তরটি যা হাড়ের সবচেয়ে প্রিয়, এর একটি rezhuzhed সংস্করণ। সহকর্মী purists / verbosity-haters ...

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

কস্টোস মর্তেমে বলেছেন:

আমি অবাক হলাম যে ব্লকিং / নন-ব্লকিং কল সমস্যাগুলিতে কেউই আসলেই প্রবেশ করেনি

নীচের পড়া NSTask সংক্রান্ত ব্লকিং / নন-ব্লকিং কল সমস্যাগুলির জন্য:

asynctask.m - NSTask এর সাথে ডেটা প্রক্রিয়াকরণের জন্য অ্যাসিড্রোনাস স্টেডিন, স্টডাউট এবং স্টডার স্ট্রিমগুলি কিভাবে প্রয়োগ করতে হয় তা নমুনা কোড।

Asynctask.m এর উত্স কোড GitHubGitHub


কেন্টের নিবন্ধ আমাকে একটি নতুন ধারণা দিয়েছে। এই 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");

ভাল পুরানো POSIX system ("echo -en '\ 007'");


যদি টার্মিনাল কমান্ডটির প্রশাসক বিশেষাধিকার প্রয়োজন হয় (ঊর্ধ্বতন sudo ), পরিবর্তে AuthorizationExecuteWithPrivileges ব্যবহার করুন। নিম্নলিখিত "com..test" নামক একটি ফাইল তৈরি করবে root ডিরেক্টরি "/ 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); 






macos