मैं 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
कार्य के मानक आउटपुट को पुनर्निर्देशित करने के लिए उपयोग किया जाता है।
आपके उद्देश्य-सी अनुप्रयोग के भीतर से ऑपरेटिंग सिस्टम के साथ बातचीत करने के बारे में अधिक विस्तृत जानकारी के लिए, आप इस दस्तावेज़ को Apple के विकास केंद्र पर देख सकते हैं: ऑपरेटिंग सिस्टम के साथ सहभागिता ।
संपादित करें: NSLog समस्या के लिए शामिल फिक्स
यदि आप bash के माध्यम से कमांड-लाइन यूटिलिटी को चलाने के लिए NSTask का उपयोग कर रहे हैं, तो आपको NSDog को रखने के लिए इस मैजिक लाइन को शामिल करने की आवश्यकता है:
//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;
kent के लेख ने मुझे एक नया विचार दिया। इस रनकमांड विधि को स्क्रिप्ट फ़ाइल की आवश्यकता नहीं है, बस एक लाइन द्वारा एक कमांड चलाता है:
- (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 ...) आवेदन को फिर से संकलित किए बिना, लेकिन सिर्फ संपादन बंडल में शेल स्क्रिप्ट।
//------------------------------------------------------
-(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 समस्या के लिए शामिल फिक्स
यदि आप bash के माध्यम से कमांड-लाइन यूटिलिटी को चलाने के लिए NSTask का उपयोग कर रहे हैं, तो आपको NSDog को रखने के लिए इस मैजिक लाइन को शामिल करने की आवश्यकता है:
//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
से फ़ाइल डेटा से आरंभ नहीं किया जा सकता है, तो फ़ंक्शन त्रुटि संदेश देता है। फ़ंक्शन को एक वैकल्पिक वापस करने के लिए लिखा जा सकता था 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()
कांटा , निष्पादन , और इंतजार करना चाहिए, अगर आप वास्तव में एक उद्देश्य-सी विशिष्ट तरीके की तलाश में नहीं हैं। 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. */
सिस्टम भी है , जो कमांड चलाता है जैसे कि आपने इसे शेल के कमांड लाइन से टाइप किया है। यह सरल है, लेकिन स्थिति पर आपका नियंत्रण कम है।
मैं मान रहा हूं कि आप मैक एप्लिकेशन पर काम कर रहे हैं, इसलिए लिंक इन कार्यों के लिए Apple के डॉक्यूमेंटेशन के हैं, लेकिन वे सभी हैं POSIX
, इसलिए आपको उन्हें किसी भी POSIX-compliant सिस्टम पर उपयोग करना चाहिए।
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 संस्करण है जो हड्डियों का सबसे अच्छा है , मेरे साथी के लिए शुद्धतावादी / वाचालता-नफरत करने वाले ...
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 के साथ डेटा को संसाधित करने के लिए अतुल्यकालिक स्टड, स्टडआउट और स्टेडर धाराओं को कैसे लागू किया जाए।
GynHub पर asynctask.m का सोर्स कोड उपलब्ध है ।
उपरोक्त कई उत्कृष्ट उत्तरों के अलावा, मैं पृष्ठभूमि में कमांड के आउटपुट को संसाधित करने और अवरुद्ध तंत्र से बचने के लिए निम्न कोड का उपयोग करता हूं [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];
}
या चूँकि ऑब्जेक्टिव C सिर्फ कुछ 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 हेडर फ़ाइल से शामिल हैं।
यदि टर्मिनल कमांड के लिए प्रशासक विशेषाधिकार (उर्फ sudo
) की आवश्यकता होती है , तो AuthorizationExecuteWithPrivileges
इसके बजाय उपयोग करें । निम्नलिखित "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);