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




objective-c cocoa (10)

আপনি 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

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


fork , exec , এবং wait উচিত, যদি আপনি সত্যিই একটি উদ্দেশ্য-সি নির্দিষ্ট ভাবে খুঁজছেন না। fork বর্তমানে চলমান প্রোগ্রামের একটি অনুলিপি তৈরি করে, চলমান চলমান প্রোগ্রামটিকে নতুন করে প্রতিস্থাপিত করে এবং উপ-প্রসারের প্রস্থান করার জন্য অপেক্ষা করে। উদাহরণস্বরূপ (কোন ত্রুটি পরীক্ষণ ছাড়া):

#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 যা কমান্ড চালায় যেমন আপনি শেলের কমান্ড লাইন থেকে এটি টাইপ করেছেন। এটা সহজ, কিন্তু পরিস্থিতির উপর আপনার কম নিয়ন্ত্রণ আছে।

আমি ম্যাক অ্যাপ্লিকেশনে কাজ করছি বলে মনে করছি, তাই এই ফাংশনগুলির জন্য অ্যাপল এর ডকুমেন্টেশন লিঙ্কগুলি আছে, কিন্তু তারা সব POSIX , তাই আপনাকে তাদের কোনও POSIX-compliant সিস্টেমে ব্যবহার করতে হবে।


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

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

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

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

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


উপরের বেশ কয়েকটি চমৎকার উত্তর ছাড়াও, আমি পটভূমিতে কমান্ডটির আউটপুট প্রক্রিয়াকরণের জন্য নিম্নলিখিত কোডটি ব্যবহার করি এবং [file readDataToEndOfFile] ব্লকিং প্রক্রিয়াটি এড়াতে [file readDataToEndOfFile]

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

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

উদ্দেশ্য-সি (সুইফ্টের জন্য নিচে দেখুন)

এটি আরও পাঠযোগ্য, কম অকার্যকর করার জন্য উপরের উত্তরটিতে কোডটি সাফ করুন, এক-লাইন পদ্ধতির সুবিধা যোগ করে এবং একটি 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()

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

অথবা যেহেতু অবজেক্ট সিটি উপরের OO লেয়ারের সাথে কেবল C হয় তাই আপনি পজিক্স কন্টার্টারগুলি ব্যবহার করতে পারেন:

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 হেডার ফাইল থেকে অন্তর্ভুক্ত করা হয়।


ভাগ করার মনোভাবের মধ্যে ... এটি শেল স্ক্রিপ্টগুলি চালানোর জন্য আমি প্রায়শই ব্যবহার করি এমন একটি পদ্ধতি। আপনি আপনার পণ্য বান্ডেল (বিল্ডের অনুলিপি পর্যায়ে) এ একটি স্ক্রিপ্ট যোগ করতে পারেন এবং তারপরে স্ক্রিপ্টটি পড়তে এবং রানটাইমতে রান করতে পারেন। নোট: এই কোডটি PrivateFrameworks উপ-পাথের স্ক্রিপ্টটির জন্য দেখায়। সতর্কতা: এটি স্থাপন করা পণ্যের জন্য একটি নিরাপত্তা ঝুঁকি হতে পারে, কিন্তু আমাদের বাড়ির বিকাশের জন্য অ্যাপ্লিকেশনটি পুনরায় সংকলিত না করে সহজ জিনিসগুলিকে কাস্টমাইজ করার একটি সহজ উপায় (যেমন হোস্ট করার জন্য ...)। বান্ডেল শেল স্ক্রিপ্ট।

//------------------------------------------------------
-(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 ব্যবহার করেন তবে 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


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


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

সুইফট 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? , কিন্তু এটি ব্যবহার করা অযৌক্তিক এবং এটি একটি দরকারী উদ্দেশ্য পরিবেশন করা হবে না কারণ এটি ঘটতে পারে এমন অসম্ভাব্য।





macos