NSTask and arguments when running command line tools - cocoa

How would I pass arguments (host in this case) to NSTask in this code? It does not accept the host NSString. If I pass the host value with the ping, for e.g..
[NSArray arrayWithObjects:#"-c",#"ping -c 5 www.google.com",nil]
then it works. But it won't take the host argument separately. Thanks for the help in advance.
task = [[NSTask alloc] init];
[pipe release];
pipe = [[NSPipe alloc] init];
[task setStandardInput: [NSPipe pipe]];
[task setLaunchPath:#"/bin/bash"];
NSArray *args = [NSArray arrayWithObjects:#"-c",#"ping -c 5",host,nil];
[task setArguments:args];
[task setStandardOutput:pipe];
NSFileHandle *fh = [pipe fileHandleForReading];

Use stringWithFormat method of NSString class
task = [[NSTask alloc] init];
[pipe release];
pipe = [[NSPipe alloc] init];
[task setStandardInput: [NSPipe pipe]];
[task setLaunchPath:#"path"];
NSArray *args = [NSArray arrayWithObjects:#"-c",[NSString stringWithFormat: #"%# %# %# %#",#"ping",#"-c",#"5",host],nil];
[task setArguments:args];
[task setStandardOutput:pipe];
NSFileHandle *fh = [pipe fileHandleForReading];

Your arguments are not correct. First of all, you should set the launchpath to /bin/ping, or wherever the task is located, then the arguments should be an array of the arguments you would normally enter on the command line, but then seperated by spaces there..
Please take a look at this tutorial Wrapping UNIX commands for more information on how to do this properly.

NSMutableArray *args = [NSMutableArray array];
NSArray *args = [NSArray arrayWithObjects:#"-c", #"\"ping -c 5", host, #"\"",nil]
[task setArguments:args];
Bash -c needs to take your command in quotes.

Related

Shell script with NSTask doesn't work

I have a command (xcodebuild) that if I try in terminal it works very well. When I try to put it on my code:
let xcodeProjectPath = "/Users/xxx/Desktop/Code/xxx.xcworkspace"
let xcodeArchivePath = "/Users/xxx/Desktop/xxx.xcarchive"
let schemeName = "XXX"
let pid = NSProcessInfo.processInfo().processIdentifier
let pipe: NSPipe = NSPipe()
let file: NSFileHandle = pipe.fileHandleForReading
let archiveCommand = "xcodebuild -scheme \(schemeName) -workspace \(xcodeProjectPath) -configuration Release -archivePath \(xcodeArchivePath) archive"
let task = NSTask()
task.launchPath = "/bin/sh"
task.arguments = NSArray(objects: "-l", "/Users/xxx/Desktop/test.sh") as [AnyObject]
task.standardInput = NSPipe()
task.standardOutput = pipe
task.launch()
let data = file.readDataToEndOfFile()
file.closeFile()
let grepOutput = NSString(data: data, encoding: NSUTF8StringEncoding)
println("Returned: \(grepOutput!)")
It doesn't work and return me:
** ARCHIVE FAILED **
The following build commands failed:
DataModelVersionCompile /Users/xxx/Library/Developer/Xcode/DerivedData/XXX-fbrisxgdcevajabbkhkejvwjrxyt/Build/Intermediates/ArchiveIntermediates/XXX/InstallationBuildProductsLocation/Applications/XXX.app/Database.momd Application/Resources/Database/Database.xcdatamodeld
My script.sh is:
#!/bin/sh
xcodebuild -scheme XXX -workspace /Users/xxx/Desktop/Codice/xxx.xcworkspace -configuration Release -archivePath /Users/xxx/Desktop/provaBuild.xcarchive archive
Any idea? :(
After many attempts I discover that if I try my code running app with Xcode it doesn't works. If i build the mac app... everything works!
What you are trying to accomplish?
You archiveCommand variable is never used and the second NSTask is initialized with an external sh file.
Anyway I think you are trying to read the pipe before the operation ends; I think waitUntilExit method just before launch command should help (or at least use the termination handler).
This is a test code with the relevant part of your question; it should works fine (I've written it in ObjC but the translation is very straightforward).
NSArray *args = #[ #"-scheme",schemePath,#"-workspace",prjPath,#"-configuration",#"Release",#"-archivePath",archPath,#"archive"];
[task setArguments:args];
NSPipe *outputPipe = [NSPipe pipe];
[task setStandardOutput:outputPipe];
[task setTerminationHandler:^(NSTask *task) {
NSFileHandle * read = [outputPipe fileHandleForReading];
NSData * dataRead = [read readDataToEndOfFile];
NSString * stringRead = [[NSString alloc] initWithData:dataRead encoding:NSUTF8StringEncoding];
NSLog(#"output: %#", stringRead);
}];
[task launch];

NSProcessInfo returns different PATH than "echo $PATH"

I am trying to programatically figure out whether there is a specific binary in the system PATH. To get the environment I used both
NSString* path = [[[NSProcessInfo processInfo] environment] objectForKey:#"PATH"];
and
NSString* path2 = [NSString stringWithUTF8String: getenv("PATH")];
both yielding the same result, in both cases different then echo $PATH in console. Both path and path2 does not contain paths set via /etc/paths.d, so the question is how to get the the environment PATH as returned from console programatically?
NSProcessInfo will just access information about current process. For example below i am executing the same echo $PATH command in cocoa and am getting the same output which NSProcessInfo is displaying. So in the terminal when you execute the same command. You will get different ouput. Because it is showing the path of current process in terminal. If you want to see the same output of both you can execute this command in terminal launchctl getenv PATH which will be equivalent to [[[NSProcessInfo processInfo] environment] objectForKey:#"PATH"];
NSTask *task;
task = [[NSTask alloc] init];
[task setLaunchPath: #"/bin/bash"];
[task setArguments:[NSArray arrayWithObjects: #"-c", #"echo $PATH",nil]];
NSPipe *pipe;
pipe = [NSPipe pipe];
[task setStandardOutput: pipe];
NSFileHandle *file;
file = [pipe fileHandleForReading];
[task launch];
NSData *data;
data = [file readDataToEndOfFile];
NSString *response = [[NSString alloc] initWithData: data encoding: NSUTF8StringEncoding];
NSLog(#"%#",response);

Forcing Python to run in 64 mode from a NSTask subprocess

What is a method that forces python to run in 64 mode from a NSTask?
Update: As per Ned's suggestion I tried referencing Python2.7 directly with objective c and that worked. Changed #"/usr/bin/python" to #"/usr/bin/python2.7". The new code is at the bottom of the question.
I have a 64 bit system. When run from terminal python runs in 64 bit.
When I run a plain shell from a NSTask running /usr/bin/uname -m it returns x86_64.
I've tried using arch but the shell running python is still in 32 bit mode.
Example method
-(void) runPython64BitScriptViaArchWithPath:(NSString*)path {
NSTask* task = [[NSTask alloc] init];
task.launchPath = #"/usr/bin/arch" ;
task.arguments = [NSArray arrayWithObjects: #"-x86_64", #"/usr/bin/python", path, nil];
[task setStandardInput:[NSPipe pipe]] ;
NSPipe *stdOutPipe = nil;
stdOutPipe = [NSPipe pipe];
[task setStandardOutput:stdOutPipe];
NSPipe* stdErrPipe = nil;
stdErrPipe = [NSPipe pipe];
[task setStandardError: stdErrPipe];
NSLog(#"%#", [task arguments]) ;
[task launch] ;
NSData* data = [[stdOutPipe fileHandleForReading] readDataToEndOfFile];
[task waitUntilExit];
NSInteger exitCode = task.terminationStatus;
if (exitCode != 0)
{
NSLog(#"Error!");
NSData *error = [[stdErrPipe fileHandleForReading] readDataToEndOfFile] ;
NSLog(#"Exit code : %ld", (long)exitCode) ;
NSString *result = [[NSString alloc] initWithBytes: error.bytes length:error.length encoding: NSUTF8StringEncoding] ;
NSLog(#"%#",result);
[result release];
} else {
NSString *result = [[NSString alloc] initWithBytes: data.bytes length:data.length encoding: NSUTF8StringEncoding] ;
NSLog(#"%#",result) ;
[result release];
}
[task release] ;
}
an example python script that works if run from terminal
#!/usr/bin/env python
import os
os.environ['DJANGO_SETTINGS_MODULE'] = 'social_shields.settings'
try:
import hosts.models
shield = hosts.models.Shield.objects.all()[0]
shield.active = True
shield.save()
except Exception as exception:
import struct
print 'Bits : %s' % ( 8 * struct.calcsize("P"))
print exception
example log
2013-07-04 16:10:31.600 socialshield[88688:303] onShieldDown
2013-07-04 16:10:31.607 socialshield[88688:303] x86_64
2013-07-04 16:10:31.607 socialshield[88688:303] (
"-x86_64",
"/usr/bin/python",
"/source/social_shields/social_shields/shield_down.py"
)
2013-07-04 16:10:31.933 socialshield[88688:303] Bits : 32
Error loading MySQLdb module: dlopen(/Library/Python/2.7/site-packages/_mysql.so, 2): no suitable image found. Did find:
/Library/Python/2.7/site-packages/_mysql.so: mach-o, but wrong architecture
new code that works after applying Ned's suggestion :-)
-(void) runPython64BitScriptViaArchWithPath:(NSString*)path {
NSTask* task = [[NSTask alloc] init];
task.launchPath = #"/usr/bin/arch" ;
task.arguments = [NSArray arrayWithObjects: #"-x86_64", #"/usr/bin/python2.7", path, nil];
[task setStandardInput:[NSPipe pipe]] ;
NSPipe *stdOutPipe = nil;
stdOutPipe = [NSPipe pipe];
[task setStandardOutput:stdOutPipe];
NSPipe* stdErrPipe = nil;
stdErrPipe = [NSPipe pipe];
[task setStandardError: stdErrPipe];
NSLog(#"%#", [task arguments]) ;
[task launch] ;
NSData* data = [[stdOutPipe fileHandleForReading] readDataToEndOfFile];
[task waitUntilExit];
NSInteger exitCode = task.terminationStatus;
if (exitCode != 0)
{
NSLog(#"Error!");
NSData *error = [[stdErrPipe fileHandleForReading] readDataToEndOfFile] ;
NSLog(#"Exit code : %ld", (long)exitCode) ;
NSString *result = [[NSString alloc] initWithBytes: error.bytes length:error.length encoding: NSUTF8StringEncoding] ;
NSLog(#"%#",result);
[result release];
} else {
NSString *result = [[NSString alloc] initWithBytes: data.bytes length:data.length encoding: NSUTF8StringEncoding] ;
NSLog(#"%#",result) ;
[result release];
}
[task release] ;
}
I assume you have verified that /Library/Python/2.7/site-packages/_mysql.so is 64-bit and that you are really using the same Python in both cases. On OS X 10.6 and later systems, /usr/bin/python is actually a wrapper executable that determines which version of Python and which architecture (32-bit or 64-bit) to run; see man 1 python for details. Try executing /usr/bin/python2.7 directly. That should default to 64-bit. You can also check whether Python is running in 32- or 64-bit mode by using this documented test:
import sys; print(sys.maxsize > 2**32)

Append data to an existing file in new line cocoa

I'm developing a Cocoa application for Mac. I have to append data of a file to an existing file in new line. I am trying to do this by following code:
NSData * theData = [NSData dataWithContentsOfFile: #"~/Desktop/test/new.rtf"
options: NSMappedRead
error: &error];
NSFileHandle *output = [NSFileHandle fileHandleForWritingAtPath:#"~/Desktop/test/test.rtf"];
[output seekToEndOfFile];
[output writeData:theData];
But this code is not working. This code is doing nothing. Neither giving any error nor writing data of file new.rtf to test.rtf. Any idea how can I append data of file new.rtf to test.rtf in new line??
NSString *readFile = [#"~/Desktop/test/new.rtf" stringByExpandingTildeInPath];
NSString *writeFile = [#"~/Desktop/test/test.rtf" stringByExpandingTildeInPath];
NSData * theData = [NSData dataWithContentsOfFile:readFile
options:NSMappedRead
error:NULL];
NSFileHandle *output = [NSFileHandle fileHandleForUpdatingAtPath:writeFile];
[output seekToEndOfFile];
[output writeData:theData];
[output closeFile];

How to execute shell-command "ping" and get result into string in Objective-C/Cocoa?

I tried this code
NSTask *task;
task = [[NSTask alloc] init];
[task setLaunchPath: #"/usr/bin/ping"];
NSArray *arguments;
arguments = [NSArray arrayWithObjects: #"-c", #"3",#"stackoverfow.com", 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 (#"ping returned:\n%#", string);
[string release];
[task release];
And got this
2011-10-21 17:34:42.805 pintTest[8819:707] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'launch path not accessible'
*** First throw call stack:
(
0 CoreFoundation 0x00007fff8acab286 __exceptionPreprocess + 198
1 libobjc.A.dylib 0x00007fff89463d5e objc_exception_throw + 43
2 CoreFoundation 0x00007fff8acab0ba +[NSException raise:format:arguments:] + 106
3 CoreFoundation 0x00007fff8acab044 +[NSException raise:format:] + 116
4 Foundation 0x00007fff8fc6c2c8 -[NSConcreteTask launchWithDictionary:] + 470
5 pintTest 0x0000000100000de0 main + 400
6 pintTest 0x0000000100000c44 start + 52
7 ??? 0x0000000000000001 0x0 + 1
)
terminate called throwing an exceptionsharedlibrary apply-load-rules all
Current language: auto; currently objective-c
Try replacing:
[task setLaunchPath: #"/usr/bin/ping"];
with
[task setLaunchPath: #"/sbin/ping"];
since that’s where ping is located.

Resources