I'm trying to call ffmpeg from NSTask in objective-c. I execute the ffmpeg command in terminal and it works flawlessly every time. I make the same command using NSTask, and it never gives me the whole output. It cuts it off half way through the output, at a seemingly random spot every time. Here is my code.
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
NSString* ffmpegPath = [[NSBundle mainBundle] pathForResource:#"ffmpeg" ofType:#""];
NSString* path = #"test.mov";
NSTask *task = [[NSTask alloc] init];
NSArray *arguments = [NSArray arrayWithObjects: #"-i", path, nil];
NSPipe *pipe = [NSPipe pipe];
NSFileHandle * read = [pipe fileHandleForReading];
[task setLaunchPath: ffmpegPath];
[task setArguments: arguments];
[task setStandardOutput: pipe];
[task launch];
[task waitUntilExit];
NSData* data = [read readDataToEndOfFile];
NSString* stringOutput = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(#"%#", stringOutput);
NSLog(#"%i", [task terminationStatus]);
NSLog(#"DONE");
}
And just like that I figured it out. Apparently the output had non UTF8Characters in it. Switched it over to NSASCIIStringEncoding and voila. Magic.
Related
I am trying to run an executable on OS X from one of the other application.
NSPipe *pipe = [NSPipe pipe];
NSTask *task = [[NSTask alloc] init];
task.arguments = #[#"param1", #"param2", #"param3", #"param4"];
task.launchPath = #"/usr/bin/myApp";
[task setStandardOutput: pipe];
[task launch];
[task waitUntilExit];
NSFileHandle *file = [pipe fileHandleForReading];
NSData *output = [file readDataToEndOfFile];
NSString *outputString = [[NSString alloc] initWithData:output encoding:NSUTF8StringEncoding];
[file closeFile];
NSLog(#"%#",outputString);`
But the output is nothing. Though, I am sure NSTask is getting executed. I think I am doing something wrong with NSPipe. But I am still not seeing the expected output.
Thanks.
How could I make an if statement that's like:
if(the output of a nstask is equal to a specific string){
//do stuff over here
}
I'm running a NSTask and it put's out the data that's coming from it in the NSLog, but how could I not show it there but store it as a NSString or something like that
This is what my task looks like
NSTask *task = [[NSTask alloc] init];
[task setLaunchPath:#"/usr/bin/csrutil"];
[task setArguments:#[ #"status" ]];
[task launch];
Any help is greatly appreciated :)
You need a pipe and a file handle to read the result.
Write a method
- (NSString *)runShellScript:(NSString *)cmd withArguments:(NSArray *)args
{
NSTask *task = [[NSTask alloc] init];
[task setLaunchPath:cmd];
[task setArguments:args];
NSPipe *pipe = [NSPipe pipe];
[task setStandardOutput: pipe];
NSFileHandle *file = [pipe fileHandleForReading];
[task launch];
NSData *data = [file readDataToEndOfFile];
return [[NSString alloc] initWithData: data encoding: NSUTF8StringEncoding];
}
and call it
NSString *result = [self runShellScript:#"/usr/bin/csrutil" withArguments:#[ #"status" ]];
I can run an executable from a known local directory within a Cocoa app like this:
// Run the server.
NSTask *task = [[NSTask alloc] init];
NSPipe *pipe = [NSPipe pipe];
NSString* scriptPath = #"/Users/example/Server/runServerExecutable.sh";
[task setLaunchPath: #"/bin/sh"];
[task setArguments: [NSArray arrayWithObjects: scriptPath, nil]];
[task setStandardOutput: pipe];
[task launch];
Could anyone help me on these questions?
Where should I include the executable/script/text files in the app bundle?
How to modify scriptPath to run the script programmatically?
Thanks a lot!
Drag and drop the script/text files to your xcode project -> Project Navigator, so that it will get added to your project. Build the project. Open the Bundle, you will be able to see the added file in Resources directory.
Now, the code given below will help you get the file from the resources. For the sake of example, I have added a script by the name "OpenSafari.sh"
NSTask *temp = [[NSTask alloc] init];
[temp setLaunchPath:#"/bin/sh"];
NSString *filePath = [[NSBundle mainBundle] pathForResource:#"OpenSafari" ofType:#"sh"];
NSArray *tempargs = [NSArray arrayWithObjects:filePath,nil];
[temp setArguments:tempargs];
NSPipe *temppipe = [NSPipe pipe];
[temp setStandardOutput:temppipe];
NSPipe *errorPipe = [NSPipe pipe];
[temp setStandardError:errorPipe];
[temp launch];
NSData *data = [[temppipe fileHandleForReading] readDataToEndOfFile];
NSString *result = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSData *dataErr = [[errorPipe fileHandleForReading] readDataToEndOfFile];
NSString *resultErr = [[NSString alloc] initWithData:dataErr encoding:NSUTF8StringEncoding];
Hope this helps!
How can I programmatically run terminal command?
Now I'm doing like this:
-(IBAction)proceedTapped:(id)sender
{
NSLog(#"%#",[self runCommand:#"cd ~"]);
NSLog(#"%#",[self runCommand:#"ls"]);
}
-(NSString *)runCommand:(NSString *)commandToRun
{
NSTask *task = [[NSTask alloc] init];
[task setLaunchPath: #"/bin/sh"];
NSArray *arguments = [NSArray arrayWithObjects:
#"-c" ,
[NSString stringWithFormat:#"%#", commandToRun],
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 *output;
output = [[NSString alloc] initWithData: data encoding: NSUTF8StringEncoding];
return output;
}
For single commands this code works well, but in my case, when I want to change directory
to ~(home) and then do 'ls' this doesn't work well (it looks like to run two commands in two different terminal windows).
So how to run multiple commands like in one terminal window ?
Thank you.
In one of my applications I have:-
- (IBAction)openDirInTerminal:(id)sender { // context only
DirectoryItem *item = (DirectoryItem *)[self.dirTree focusedItem];
NSString *s = [NSString stringWithFormat:
#"tell application \"Terminal\" to do script \"cd \'%#\'\"", item.fullPath];
NSAppleScript *as = [[NSAppleScript alloc] initWithSource: s];
[as executeAndReturnError:nil];
}
DirectoryItem *item = (DirectoryItem *)[self.dirTree focusedItem]; is my method, but replace item.fullPath with a Path.
You could add the 2nd command to the script string (followed by a newline).
In my program I currently use NSTask 5 times, and it all works very well, but I'm tired of having to repeat so much code when it's all so similar, so I tried putting it in a function. Unfortunately it results in a crash on the line: [task launch]. Other than that I can't figure out what's causing the crash as if I use this code outside the function it works perfectly.
The method I am using is as follows:
- (NSString *)performTask: (NSString *)launchPath: (NSString *)argument1: (NSString *)argument2: (NSString *)argument3: (NSString *)argument4: (NSString *)argument5
{
NSString *resPath = [[NSBundle mainBundle] resourcePath];
NSTask *task = [[NSTask alloc] init];
[task setLaunchPath: [NSString stringWithFormat: launchPath, resPath]];
NSArray *arguments = [NSArray arrayWithObjects: argument1, argument2, argument3, argument4, argument5, nil];
[task setArguments: arguments];
NSPipe *pipe = [NSPipe pipe];
[task setStandardInput:[NSPipe pipe]];
[task setStandardOutput: pipe];
NSFileHandle *file = [pipe fileHandleForReading];
[task launch];
NSData *data = [file readDataToEndOfFile];
NSString *status = [[NSString alloc] initWithData: data encoding: NSUTF8StringEncoding];
return status;
}
I really hope this can be fixed, I really cannot see why this crashes.
Thanks in advance everyone.
Check this out it's pretty cool I use it:
https://gist.github.com/1875386
It's also much easier than using arrayWithObjects: for NSTask...
rc