Executing shell commands with NSTask - Objective-C Cocoa - objective-c

I have been searching for days and hours for this, I have seen a lot of examples of this, but cannot figure out how NSTask works, let's say I wanted to execute the command killall Dock
or defaults write com.apple.Finder AppleShowAllFiles YES something like that, how would I go about doing this.
I know how to execute an external shell script (sh) but need to be more sophisticated and use NSTask instead.
Thanks for any help!!

You could do something like:
NSTask *task = [[NSTask alloc] init];
[task setLaunchPath:#"/bin/bash"];
[task setArguments:#[ #"-c", #"/usr/bin/killall Dock" ]];
[task launch];
Exactly what launch path and arguments you provide are dictated by the command you want to run and its parameters.

Related

Getting a list of all bootable drives mac

I'm currently looking for a way to get a list of all the bootable partitions on a Mac?
I know you can get a list of all the volumes? But I don't know how to check if each particular volume is bootable?
Is there a way to do this using swift or objective c?
With objective-c or swift probably not, but you can run an apple script (terminal commands) from your objective-c app (on OS X, I don't know whether it can be done on iOS)
This is how you execute an apple script:
//Begin of the script
NSAppleScript *script = [[NSAppleScript alloc]
initWithSource:#"Tell application \"Terminal\" \n\
do shell script \" some script here \"\n\
end tell"];
NSDictionary *errors = nil;
NSAppleEventDescriptor *result = [script executeAndReturnError:&errors];
NSLog(#"result: %#", result);
NSLog(#"errors: %#", errors);
if(errors==NULL){
NSLog(#"Succeeded");
}
else{
NSLog(#"Failed");
}
//End of the script
The quick and dirty way to do this is to shell out to either bless --info or systemsetup liststartupdisks commands. You can specify that you want the output in plist format to make it easier to parse as well.
As far as executing those commands the typical NSTask should work. You could also use system() I suppose, but it isn't really standard practice on OS X as NSTask has many advantages over it.
A purely code way to do this would be to get the list of disks and then look at each them for the known files that make OS X bootable. Things like the boot.efi file in /System/Library/CoreServices, the mach kernel file, and the contents of /System/Library/CoreServices/SystemVersion.plist.

system() call in cocoa app

In my cocoa app I have to call system() function to launch an external app. The command I use is:
system("./main &");
If I run the app from Xcode, it works fine, because I know the folder where to put main.
If I create an archive, and distribute my .app application, system() can't find "main". Where I have to put it?? Or otherwise, how can I run an app using "./" when I'm not in the folder the application is?
EDIT: Maybe I solved using NSTask, but how can I run "main" in background? Now it opens in a new terminal window.
NSTask *task = [[NSTask alloc] init];
[task setLaunchPath:#"/Applications/Multibo/main"];
[task setArguments:[NSArray arrayWithObjects:[NSString stringWithFormat:#"./main &"], nil]];
[task launch];
Thanks
Try appending the full path before the executable's name and use it as an argument to system. Note that system is implementation defined -- its behavior is not guranteed to be the same across systems and its usage is thus not recommended. You should probably look for a suitable alternative such as NSWorkspace.
While dirkgently's answer is directly correct, the real answer is more complex.
First, NSTask is not a generic command line invoker. That is why adding & doesn't do what you expect. In fact, all tasks invoked via NSTask are effectively background.
But you really don't want to use NSTask. You should really be using an XPC service. Now, if your goal is something that runs even after your program exits, you should be looking into LaunchServices.

How to run Objective-C binary on a web server?

Alright, I have a rather odd question here. I feel much more comfortable writing code in Objective-C over any other language. I recently had to do some server-side programming, which required me to learn PHP. It works, yeah, but for fun I want to achieve the same thing through Objective-C. So, I created a binary using Xcode's Foundation preset. Here's most of the binary:
#import <Foundation/Foundation.h>
#import "JSONKit.h"
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
NSString *theURL = [NSString stringWithFormat:#"http://blahblahblah.com/blah"];
NSError *err = nil;
NSURLResponse* response = nil;
NSMutableURLRequest* request = [[[NSMutableURLRequest alloc] init] autorelease];
NSURL*URL = [NSURL URLWithString:theURL];
[request setURL:URL];
[request setCachePolicy:NSURLRequestReloadIgnoringLocalCacheData];
[request setTimeoutInterval:30];
NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&err];
NSDictionary *someData = [data objectFromJSONData];
NSString *someString = [[someData objectForKey:#"foo"]objectForKey:#"bar"];
//do something
[pool drain];
return 0;
}
Quite basic code. It simply downloads some stuff from my server and I parse the JSON result and get a string that I want to use. So, my question is - how can I run this on my Linux-based server? I know it's possible, maybe using GNUStep (or cocotron?), which I don't know how to use. Anyone have ideas?
Well,
I suggest the same thing as the #lacqui.. Use CGI to run your program.. and here's the steps ..
(Side note: using CGI is deprecated as it starts a process each time a request is coming to the server (modern servers/web containers initiate a new thread (vs process).)
So, let's Start:
The input at hand is a program written in Objectiv-c
The output is a CGI script(program or whatever they name it) that
will run inside some http server.
First, let me ask you, What's the target platform to deploy your application?
If target deployment platform is a Mac then you will have to get the binary out of xcode ( I think it would be in a .dmg format) and find some where how to run a .dmg as a CGI program inside a web server ( I am not sure if apache runs under Mac or not)
But If it is Windows or Linux:
You will need to compile your application using GNUstep (I know nothing about portability from Xcode to GNUstep) You will need a GNUstep. Steps to install GNUstep either for Windows or Linux is trivial.
Once Installing GNUstep, you will have to compile your application again using it, refer to the same two links above to know how to compile your application.
The issue here is, AFAIK, GNUstep don't fully support Objc-2, so possibilities that the compilation will fail cause of usage of JSONKit.h is high. If your program compiles successfully, then you are almost done.
Suppose your program compiles, And you now have the binary program.. You will need to deploy it in some HTTP server that have CGI enabled. You can follow my blogpost here to know how to deploy a binary program written in C into some small http server called mini-httpd on Linux (it should apply to any binary program regardless of its source language).
What you want to look at is called the Common Gateway Interface. It is a protocol that states the way a web server will interact with subordinate processes.
What will happen is that, when a user browses to the URL that is mapped to your program, the server will start your program, and put the text of the request into STDIN. Your program will do whatever processing is required, then put the results (as well as some header information) into STDOUT.
What goes wrong when you try? You should be able to compile it with the GCC's Objective-C compiler. You should be able to run it.

Cocoa/ Objective-C Shell Command Line Execution

This is probably a stupid question, but how can I execute a shell command from my Cocoa app?
I have the command as a string "command", but can easily manipulate data as needed.
There is no need to get a returned output value.
NSTask is pretty easy to do this with. For a synchronous call, you can use something like this fragment:
NSString *path = #"/path/to/executable";
NSArray *args = [NSArray arrayWithObjects:..., nil];
[[NSTask launchedTaskWithLaunchPath:path arguments:args] waitUntilExit];
The -waitUntilExit call makes sure it finishes before proceeding. If the task can be asynchronous, you can remove that call and just let the NSTask do it's thing.
If you just want to run something and don't care about the output or return code (for example, you want to touch a file), you can just do
system("touch myfile.txt");
Easy as that.
NSTask
Using the NSTask class, your program can run another program as a subprocess and can monitor that program’s execution.

How lightweight is NSOperationQueue on Snow Leopard?

I'm working with some code that does a bunch of asynchronous operating with various callbacks; Snow Leopard has made this incredibly easy with blocks and GCD.
I'm calling NSTask from an NSBlockOperation like so:
[self.queue addOperationWithBlock:^{
NSTask *task = [NSTask new];
NSPipe *newPipe = [NSPipe new];
NSFileHandle *readHandle = [newPipe fileHandleForReading];
NSData *inData = nil;
[task setLaunchPath:path];
[task setArguments:arguments];
[task launch];
while ((inData = [readHandle availableData]) && [inData length]) {
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
// callback
}];
}
[task waitUntilExit];
}];
This approach works perfectly. It's like magic, as long as my callbacks handle the concurrency correctly.
Now, I want to be able to coalesce some of these calls; this is inside a model object's "refresh" method and may take a long time to complete. Having the user pound on the refresh button shouldn't tie up the machine and all that.
I can see an implementation dilemma here. I can make a whole bunch of queues - one per call type - and set their concurrent operation counts to 1 and then call -cancelAllOperations whenever it's time for a new call.
Alternately, I could do some more manual bookkeeping on which calls are currently happening and manage a single queue per model object (as I'm doing) or I could go even further and use a global queue.
How heavy is NSOperationQueue? Is creating a lot of queues a bad architecture decision? Is there a better way to coalesce these tasks?
If you're concerned about performance, don't guess: measure and then fix any bottlenecks you find. Adding queues is simple; try it and see what Instruments tells you about the effect on performance.
The main reason for creating multiple queues is in case you have some reason for wanting to start and stop them. If you just want to get the benefits of libdispatch, you can get that by just adding operations to the main queue.
You can add multiple blocks
to an NSBlockOperation which will be executed concurrently and can be canceled by
canceling the containing operation. As long as your individual tasks don't have to be serialized, this may work.
Just use as many operation queues as you like. They are here to separate logical parts of your program. I don't think you should be too concerned about the performance as long as you aren't allocating hundreds of queues per second.