How to unzip file via terminal in Objective-c - objective-c

I want to unzip the file via terminal on Mac instead of using ZipArchive or SSZipArchive.
In terminal, I tried "unzip" command and it works good, but I do not how to express via objective c code.
I've tried this way (link: Unzip without prompt) It works but only unzipped half of my files instead of all the files.
Thanks !!

Have you tried the system() function?
system("unzip -u -d [destination full path] [zip file full path]");
You'll need to construct an NSString with your full command (including the file paths), and turn it into a C string for the system command, something like this:
NSString *myCommandString =
[NSString stringWithFormat:#"unzip -u -d %# %#", destinationPath, zipPath];
system([myCommandString UTF8String]);
This won't return any of the command's output, so you'd be better off with the solution from the Unzip without prompt question if you want details about how the operation went, but if your project doesn't need error-handling this should be fine.

See the following. I've revised a bit.
- (void)unzipme {
NSTask *task = [[NSTask alloc] init];
NSMutableString *command = [[NSMutableString alloc] initWithString:#""];
NSArray *args;
[task setLaunchPath:#"/bin/sh"];
[command appendString:#"unzip "];
[command appendString:[self convertShell:sourcePath];
[command appendString:#" "];
[command appendString:-d ];
[command appendString:[self convertShell:[self exportPath]]];
args = [NSArray arrayWithObjects:#"-c",command,nil]; // Line 10
[task setArguments:args];
NSPipe *pipe1;
pipe1 = [NSPipe pipe];
[task setStandardOutput: pipe1];
[task launch];
[task waitUntilExit];
}
- (NSString *)convertShell: (NSString *)path {
static NSString *chr92 = #"\\";
NSMutableString *replace = [[NSMutableString alloc]initWithString:chr92];
[replace appendString:#" "];
NSString *sPath = [self Replace:path :#" " :replace];
return sPath;
}
convertShell converts the Objective-C path to Shell path. Moreover, according to the unzip Man Page, this command-line tool takes a switch (-d) to specify a directory where to unzip an archive. sourcePath is a source zip file to unzip. exportPath is a destination folder. If you get an error, insert NSLog(#"%#",command); before Line 10 and show me what the command says.

Related

How check if output of bash script contains a certain string in Objective-C?

I would like to do something if the output of a shell script contains the string "Caddy 2 serving static files on :2015". This is what I have so far but the beach ball is just spinning. It seems it is because of the last command in my bash script which starts a server. There is no exit in the bash script so it does not end. The output of the bash script is correct and I can see "Caddy 2 serving static files on :2015". But it seems this code is only working with a bash script which has a end.
If someone wants to try, here you can download the executable caddy file which I am using in my bash script: https://caddyserver.com/download
- (IBAction)localHost:(id)sender
{
// execute bash script to start server
NSTask *task = [[NSTask alloc] init];
[task setLaunchPath:#"/bin/bash"];
[task setArguments:[NSArray arrayWithObjects:[[NSBundle mainBundle] pathForResource:#"caddy-start" ofType:#""], nil]];
NSPipe *pipe = [NSPipe pipe];
[task setStandardOutput: pipe];
NSFileHandle *file = [pipe fileHandleForReading];
[task launch];
NSData *data = [file readDataToEndOfFile];
NSString *result = [[NSString alloc] initWithData: data encoding: NSUTF8StringEncoding];
NSLog(result);
NSString *string = result;
NSString *substring = #"Caddy 2 serving static files on :2015";
if ([string rangeOfString:substring].location != NSNotFound) {
NSLog(#"Yes it does contain that substring");
}
else if ([string rangeOfString:substring].location == NSNotFound) {
NSLog(#"No it does NOT contain that substring");
}
}
And here is my bash script:
#!/bin/bash
DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
cd "${DIR}"
pwd
#open http://localhost:2015
# kill server if already running
kill -9 $(lsof -ti:2015)
(./caddy_darwin_amd64 stop) &
(./caddy_darwin_amd64 file-server --listen :2015 --root Content/) &

NSFileManager or NSTask moving filetypes

I've been struggling to find a solution to do what should be a very simple task. I need to move a certain type of file (all zip files in this case) into another directory. I've tried NSTask and NSFileManager but have come up empty. I can move one at a time, but I would like to move them in one shot, at the same time.
- (void)copyFilesTo :(NSString*)thisPath {
NSFileManager *manager = [NSFileManager defaultManager];
NSDirectoryEnumerator *direnum = [manager enumeratorAtPath:thisPath];
NSString *filename = nil;
while ((filename = [direnum nextObject] )) {
if ([filename hasSuffix:#".zip"]) {
[fileManager copyItemAtPath:thisPath toPath:newPath];
}
}
}
FAILED - files copied = zeroooo
- (void)copyFilesMaybe :(NSString*)thisPath {
newPath = [newPath stringByAppendingPathComponent:fileName];
task = [[NSTask alloc] init];
[task setLaunchPath: #"/usr/bin/find"];
[task waitUntilExit];
NSArray *arguments;
arguments = [NSArray arrayWithObjects: thisPath, #"-name", #"*.zip", #"-exec", #"cp", #"-f", #"{}", newPath, #"\\", #";", nil];
[task setArguments: arguments];
NSPipe *pipe;
pipe = [NSPipe pipe];
[task setStandardOutput: pipe];
NSFileHandle *file;
file = [pipe fileHandleForReading];
[task launch];
}
Same sad result, no files copied. What the heck am I doing wrong?
In the first case, you aren't using filename in your copy call. You need to construct a full path to the file by combining filename with thisPath and attempting to copy that. Also, the method is -copyItemAtPath:toPath:error:. You left off the last parameter. Try:
NSError* error;
if (![fileManager copyItemAtPath:[thisPath stringByAppendingPathComponent:filename] toPath:newPath error:&error])
// handle error (at least log error)
In the second case, I think your arguments array is wrong. I'm not sure why it includes #"\\". I suspect because at a shell you have to escape the semicolon with a backslash (\;). However, the need to escape the semicolon is because the shell would otherwise interpret it and not pass it to find. Since you're not using a shell, you don't need to do that. (Also, if you did need to escape it, it shouldn't be a separate element of the arguments array. It would be in the same element as the semicolon, like #"\\;".)
Also, are you sure the task has completed? You show the launch but you don't show observing or waiting for its termination. Given that you've set a pipe for its output, you have to read from that pipe to be sure that the subprocess isn't getting stuck writing to it.
I'm not sure why you're calling -waitUntilExit before launching the task. That may be harmless, though.

NSTask launch path not accessible. Works in Xcode. Error shown out of XCode

Ok. There are several questions on stack overflow about this. This question was the only question comes closest to mines, but it uses notifications.
The code is very simple. Create a new empty Mac OSX project and just paste the following code in the applicationDidFinishLaunching: method. It supposed to get the path of any executable file (in this case GIT).
NSTask *aTask = [[NSTask alloc] init];
NSPipe *outputPipe = [NSPipe pipe];
NSPipe *errorPipe = [NSPipe pipe];
[aTask setStandardOutput: outputPipe];
[aTask setStandardError: errorPipe];
[aTask setArguments:[NSArray arrayWithObject:#"which"]];
[aTask setLaunchPath:#"/usr/bin/git"];
NSFileHandle *outputFileHandler = [outputPipe fileHandleForReading];
NSFileHandle *errorFileHandler = [errorPipe fileHandleForReading];
[aTask launch];
[aTask waitUntilExit];
// Task launched now just read and print the data
NSData *data = [outputFileHandler readDataToEndOfFile];
NSString *outPutValue = [[NSString alloc] initWithData: data encoding: NSUTF8StringEncoding];
NSData *errorData = [errorFileHandler readDataToEndOfFile];
NSString *errorValue = [[NSString alloc] initWithData:errorData encoding:NSUTF8StringEncoding];
NSLog(#"Error value: %#",errorValue);
NSLog(#"Output Value: %#",outPutValue);
This code sets up two reading pipes and runs one command: which git.
If i run this in XCode i get this results corretly:
Error value: ""
Output Value: /usr/bin/git
If i go to my build/Products/Debug folder and double click the executable file, i get this message printed on the console app:
Question: So, what is really the problem here? please just dont make an alternative solution... I also want to know what the problem is.. thanks.
OK turns out the answer is on stack overflow, but its spread across different questions.
The question was asked here -> Commands with NSTask and here -> NSTask launch path not accessible as well
But their answers as of this date arent clear as to what the problem was. It's only after reading the question from NSTask not picking up $PATH from the user's environment (the question's title was misleading) and with these two answers NSTask not picking up $PATH from the user's environment and Find out location of an executable file in Cocoa that I realized the solution.
It looks like this is about setting up either NS
Task or the user's shell (e.g., ~/.bashrc) so that the correct
environment ($PATH) is seen by NSTask.
Solution:
[task setLaunchPath:#"/bin/bash"];
NSArray *args = [NSArray arrayWithObjects:#"-l",
#"-c",
#"which git", //Assuming git is the launch path you want to run
nil];
[task setArguments: args];
However this assumes the user's shell is always bash and it will fail for others. Solve this by determining the shell.
NSDictionary *environmentDict = [[NSProcessInfo processInfo] environment];
NSString *shellString = [environmentDict objectForKey:#"SHELL"];

New Homebrew Gui mac os project, learning objective-c

I am trying to build a Gui for homebrew on mac , with objective-c, but when i try to see the installed packages with the following code it return empty but if i try other command like update it gives me the result, I tried the same with java and the same error occurs.
Git page: feel free to help the project, the code might have a lot of errors I am new to objective-c.
NSTask *task;
task=[[NSTask alloc]init];
[task setLaunchPath:#"/Users/rogeriop062/homebrew/bin/brew"];
NSArray *arguments;
arguments = [NSArray arrayWithObjects:#"list",nil];
[task setArguments: arguments];
NSPipe *pipe;
pipe =[NSPipe pipe];
[task setStandardOutput:pipe];
NSFileHandle *file;
file=[pipe fileHandleForReading];
[task launch];
NSMutableData *data=[NSMutableData dataWithCapacity:1000];
while ([task isRunning]) {
[data appendData:[file readDataToEndOfFile]];
}
[data appendData:[file readDataToEndOfFile]];
NSString *string;
string =[[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];
NSLog(#"result: %#\n",string);
That's an interesting project; good for you! Homebrew works very nicely on my Mac. I can't see anything wrong with your code. I pasted your code into a test routine on my iMac and it worked perfectly. It listed the programs that I had installed with brew. One per line, which I didn't expect, but it worked. Sorry.
You could also try this. Delete everything in your method from[task launch] to the end, and replace it with this:
task.terminationHandler = ^(NSTask *blockTask) {
NSMutableData *data=[NSMutableData dataWithCapacity:1000];
[data appendData:[file readDataToEndOfFile]];
NSString * string =[[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];
NSLog(#"result: %#\n",string);
};
[task launch];
and that produces the same results as your code on my machine - it works, I'm afraid - but it will not take compute time waiting for the result.

Clang NSTask with streams

Never-mind all the "why?","useless?", and "don't bother" comments. I want to compile a program inside another program using clang. I can create the NSTask and set up the arguments and it will work if the file exists, (ie. no stream), and writes to a physical file. I haven't been able to get what I would really like which is to use streams for both input and output. I know that both clang and gcc allow for compiling stdin if you use the -xc and - options but am unable to implement that feature using pipes. I am also not sure how to redirect clang's output to a file handle or stream.
Here is the code I have that compiles it and generates the correct output in outfile
task = [[NSTask alloc] init];
NSPipe* outputPipe = [[NSPipe alloc] init];
[task setStandardOutput:outputPipe ];
[task setStandardError: [task standardOutput]];
NSPipe* inPipe = [NSPipe pipe];
[task setStandardInput:inPipe];
[task setLaunchPath:#"/usr/bin/clang"];
NSString* outfile= [NSString stringWithFormat:#"%#.out",[[filename lastPathComponent] stringByDeletingPathExtension]];
//[data writeToFile:#"file.c" atomically:YES];
[task setArguments:[NSArray arrayWithObjects:filename,#"-S",#"-o",outfile,nil]];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(getData:)
name: NSFileHandleReadCompletionNotification
object: [[task standardOutput] fileHandleForReading]];
[[[task standardOutput] fileHandleForReading] readInBackgroundAndNotify];
[task launch];
I have tried using this for the input stream:
/* on pipe creation*/
dup2([[inPipe fileHandleForReading] fileDescriptor], STDIN_FILENO);
NSFileHandle* curInputHandle = [inPipe fileHandleForWriting];
/* tried before launch and after, no output just sits */
[curInputHandle writeData:[NSData dataWithContentsOfFile:filename]];
Sometimes, I assume when the pipe closes while the NSTask still in existance the output file is created and will run. This makes me think that clang is just waiting for stdin to close. Is there a way to close the pipe when the data has been read?
For output I have tried to use NSPipe's fileHandleForWriting as the parameter of -o, That gives an error of [NSConcretePipe fileSystemRepresentation] unrecognized selector. I have tried creating a file handle with the file descriptor of stdout to the same error. I don't know of any command line argument that redirects it. I've tried using | to redirect but haven't been able to get it to work. If there is any unix magic to redirect it I can dup stdout to anywhere I want.
So is there any way to close a pipe when all the data it is read? And Redirect clangs output? If there is any other way to accomplish the same thing easier or cleaner I am open to any implementation.
Any help on these two items would be so great.
It is not clear to me what your problem is or what you've tried. However, if you are going to read the output from a pipe on your main thread using notifications and wish to also write to a pipe one option is to write to the pipe in another thread. The code below, based on your code, does this using GCD. For simplicity in this example the binary is deposited in /tmp:
// send a simple program to clang using a GCD task
- (void)provideStdin:(NSFileHandle *)stdinHandle
{
dispatch_queue_t aQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(aQueue, ^{
[stdinHandle writeData:[#"int main(int argc, char **argv)\n" dataUsingEncoding:NSUTF8StringEncoding]];
[stdinHandle writeData:[#"{\n" dataUsingEncoding:NSUTF8StringEncoding]];
[stdinHandle writeData:[#" write(1, \"hello\\n\", 6);\n" dataUsingEncoding:NSUTF8StringEncoding]];
[stdinHandle writeData:[#"}\n" dataUsingEncoding:NSUTF8StringEncoding]];
[stdinHandle closeFile]; // sent the code, close the file (pipe in this case)
});
}
// read the output from clang and dump to console
- (void) getData:(NSNotification *)notifcation
{
NSData *dataRead = [[notifcation userInfo] objectForKey:NSFileHandleNotificationDataItem];
NSString *textRead = [[NSString alloc] initWithData:dataRead encoding:NSUTF8StringEncoding];
NSLog(#"read %3ld: %#", (long)[textRead length], textRead);
}
// invoke clang using an NSTask, reading output via notifications
// and providing input via an async GCD task
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
NSTask *task = [NSTask new];
NSPipe *outputPipe = [NSPipe new];
[task setStandardOutput:outputPipe];
[task setStandardError:outputPipe];
NSFileHandle *outputHandle = [outputPipe fileHandleForReading];
NSPipe* inPipe = [NSPipe pipe];
[task setStandardInput:inPipe];
[task setLaunchPath:#"/usr/bin/clang"];
[task setArguments:[NSArray arrayWithObjects:#"-o", #"/tmp/clang.out", #"-xc",#"-",nil]];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(getData:)
name:NSFileHandleReadCompletionNotification
object:outputHandle];
[outputHandle readInBackgroundAndNotify];
[task launch];
[self provideStdin:[inPipe fileHandleForWriting]];
}