আমি grep
আমার অবজেক্টিভ-সি কোকো অ্যাপ্লিকেশন থেকে টার্মিনাল কমান্ড (লাইক ) কীভাবে কার্যকর করতে পারি ?
/usr/bin
যেখানে যেখানে grep
বাস করে সেখানে কোনও বাইনারি চালাতে পারে ।
আমি grep
আমার অবজেক্টিভ-সি কোকো অ্যাপ্লিকেশন থেকে টার্মিনাল কমান্ড (লাইক ) কীভাবে কার্যকর করতে পারি ?
/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
কার্যটির স্ট্যান্ডার্ড আউটপুট পুনর্নির্দেশের জন্য ব্যবহৃত হয়।
আপনার উদ্দেশ্য-সি প্রয়োগের মধ্যে থেকেই অপারেটিং সিস্টেমের সাথে ইন্টারঅ্যাক্ট করার আরও বিশদ তথ্যের জন্য আপনি অ্যাপল এর বিকাশ কেন্দ্রটিতে এই নথিটি দেখতে পাবেন: অপারেটিং সিস্টেমের সাথে ইন্টারঅ্যাক্টিং ।
সম্পাদনা: এনএসএলগ সমস্যার সমাধান অন্তর্ভুক্ত
আপনি যদি ব্যাশের মাধ্যমে কমান্ড-লাইন ইউটিলিটি চালানোর জন্য এনএসটিস্ককে ব্যবহার করছেন, তবে এনএসএলগকে কাজ করতে আপনাকে এই ম্যাজিক লাইনটি অন্তর্ভুক্ত করতে হবে:
//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]];
যখন কিছুক্ষণের লুপটি প্রস্থান করার পরে আপনার আরও একটি হওয়া উচিত ।
task.standardError = pipe;
কেন্টের নিবন্ধটি আমাকে একটি নতুন ধারণা দিয়েছে। এই রানকমন্ড পদ্ধতিতে স্ক্রিপ্ট ফাইলের দরকার নেই, কেবল একটি লাইনের মাধ্যমে একটি কমান্ড চালায়:
- (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");
ভাগ করে নেওয়ার চেতনায় ... এটি শেল স্ক্রিপ্টগুলি চালানোর জন্য আমি প্রায়শই ব্যবহার করি। আপনি আপনার পণ্য বান্ডেলে (বিল্ডের অনুলিপি পর্যায়ে) একটি স্ক্রিপ্ট যুক্ত করতে পারেন এবং তারপরে স্ক্রিপ্টটি রানটাইম পড়তে এবং চালাতে পারেন। দ্রষ্টব্য: এই কোডটি ব্যক্তিগত ফ্রেমওয়ার্কগুলি উপ-পথে স্ক্রিপ্টটির সন্ধান করে। সতর্কতা: এটি মোতায়েন করা পণ্যগুলির জন্য একটি সুরক্ষা ঝুঁকি হতে পারে, তবে আমাদের অভ্যন্তরীণ বিকাশের জন্য অ্যাপ্লিকেশনটিকে পুনরায় সংকলন না করে সহজ জিনিসগুলি (যেমন হোস্টের কাছে রিসাইঙ্ক করা যায় ...) কাস্টমাইজ করার সহজ উপায়, তবে কেবল সম্পাদনা সম্পাদনা করা বান্ডিল শেল স্ক্রিপ্ট।
//------------------------------------------------------
-(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);
}
//------------------------------------------------------
সম্পাদনা: এনএসএলগ সমস্যার সমাধান অন্তর্ভুক্ত
আপনি যদি ব্যাশের মাধ্যমে কমান্ড-লাইন ইউটিলিটি চালানোর জন্য এনএসটিস্ককে ব্যবহার করছেন, তবে এনএসএলগকে কাজ করতে আপনাকে এই ম্যাজিক লাইনটি অন্তর্ভুক্ত করতে হবে:
//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
এটি উপরের কালিদের উদ্দেশ্য-সি উত্তরের উপর ভিত্তি করে। তিনি যেমন লিখেছেন বিভাগ উপর 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
।
সুতরাং, যতক্ষণ না এটি শূন্য হয় না, ফলাফলটি একটি সুইফ্ট হিসাবে কাস্ট করতে পারে String
এবং ফিরে আসতে পারে।
যদি কোনও কারণে NSString
ফাইলের ডেটা থেকে শুরু করে না দেওয়া যায় তবে ফাংশনটি একটি ত্রুটি বার্তা দেয় returns Functionচ্ছিকটি ফিরিয়ে দেওয়ার জন্য ফাংশনটি রচনা করা যেতে String?
পারে তবে এটি ব্যবহার করা বিশ্রী হবে এবং এটি একটি কার্যকর উদ্দেশ্যটি উপস্থাপন করবে না কারণ এটির সম্ভাবনা খুব কম।
এটিকে আরও পঠনযোগ্য, কম রিডন্ড্যান্ট করার জন্য শীর্ষের উত্তরে কোডটি পরিষ্কার করা হয়েছে, এক-লাইন পদ্ধতির সুবিধা যুক্ত করে একটি এনএসএস স্ট্রিং বিভাগে তৈরি করা হয়েছে
@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()
আপনি যদি সত্যই কোনও উদ্দেশ্য-সি নির্দিষ্ট উপায়ের সন্ধান না করেন তবে কাঁটাচামচ , এক্সিকিউট এবং অপেক্ষা করা উচিত। 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. */
এর রয়েছে সিস্টেম , যা কমান্ডটি চালায় যেন আপনি শেলের কমান্ড লাইন থেকে টাইপ করেন। এটি সহজ, তবে পরিস্থিতির উপর আপনার নিয়ন্ত্রণ কম control
আমি ধরে নিচ্ছি আপনি ম্যাক অ্যাপ্লিকেশনটিতে কাজ করছেন, সুতরাং লিঙ্কগুলি এই ফাংশনগুলির জন্য অ্যাপলের ডকুমেন্টেশনের লিঙ্কগুলি হ'ল তবে সেগুলি সবই POSIX
তাই আপনার কোনও পসিএক্স-কমপ্লায়েন্ট সিস্টেমে এগুলি ব্যবহার করা উচিত।
এছাড়াও পুরাতন ভাল পসিক্স সিস্টেম রয়েছে ("প্রতিধ্বনিত '\ 007'");
Incorrect NSStringEncoding value 0x0000 detected. Assuming NSStringEncodingASCII. Will stop this compatibility mapping behavior in the near future.
আমি এই "সি" ফাংশনটি লিখেছি, কারণ 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 সংস্করণ হাড় 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]; });
}
কাস্টোস মর্টেম বলেছেন:
আমি অবাক হয়েছি কেউই সত্যিই ব্লক / নন-ব্লকিং কল সম্পর্কিত সমস্যার মধ্যে পড়ে নি
NSTask
নীচের পড়া সম্পর্কিত ব্লক / অ-ব্লকিং কল সমস্যাগুলির জন্য :
asynctask.m - নমুনা কোড যা এনএসটিস্কের সাথে ডেটা প্রসেসিংয়ের জন্য অ্যাসিনক্রোনাস স্টিডিন, স্টাডআউট এবং স্টডার স্ট্রিমগুলি কীভাবে প্রয়োগ করতে পারে তা দেখায়
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];
}
বা অবজেক্টিভ সি যেহেতু উপরের কয়েকটি ওও স্তর সহ কেবল সি হয় আপনি পিক্সিক্স সংযুক্তিগুলি ব্যবহার করতে পারেন:
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
পরিবর্তে ব্যবহার করুন instead নিম্নলিখিতটি "com.stackoverflow.test" নামে একটি ফাইল তৈরি করবে মূল ডিরেক্টরি "/ সিস্টেম / গ্রন্থাগার / ক্যাশে"।
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);