• Open Menu Close Menu
  • Apple
  • Shopping Bag
  • Apple
  • Mac
  • iPad
  • iPhone
  • Watch
  • TV
  • Music
  • Support
  • Search apple.com
  • Shopping Bag

Lists

Open Menu Close Menu
  • Terms and Conditions
  • Lists hosted on this site
  • Email the Postmaster
  • Tips for posting to public mailing lists
Re: Re: Using NSTask and NSPipe to perform a shell script (SOLVED)
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Re: Using NSTask and NSPipe to perform a shell script (SOLVED)


  • Subject: Re: Re: Using NSTask and NSPipe to perform a shell script (SOLVED)
  • From: "Michael Ash" <email@hidden>
  • Date: Wed, 6 Sep 2006 22:28:03 -0400

On 9/6/06, Keith Blount <email@hidden> wrote:
A very big thank you to all of those who took the time
to answer this. With the help you all gave me, I was
able to get this working. The main thing was setting
-setEnvironment: as Spencer pointed out.

The following code did the trick:

This is pretty much the right idea, but there are various specifics that need to be corrected before this code would be robust enough to be used in a real application. I've added specific commentary in with the code.

- (void)test
{
    NSBundle *bundle = [NSBundle mainBundle];
        NSString *latexPath = [bundle
pathForAuxiliaryExecutable:@"md2latex.sh"];

    NSTask *task = [[NSTask alloc] init];
    [task setLaunchPath:latexPath];

        [task setArguments:[NSArray
arrayWithObject:[latexPath
stringByDeletingLastPathComponent]]];

        char* path = getenv("PATH");
        NSString *newpath = [[NSString alloc]
initWithCString: path];

This will fail if the current PATH contains any non-ASCII characters. While this is relatively unlikely, it's entirely possible. If you want to avoid questions of encoding, you can just ask NSProcessInfo for the environment in NSDictionary form.

        newpath = [newpath stringByAppendingString:[@":"
stringByAppendingPathComponent:[latexPath
stringByDeletingLastPathComponent]]];

This will fail if the path to your application contains a colon. This is not that unlikely.

I believe the same thing could be accomplished by simply adding . to
the PATH and then setting the task's current directory path to the
above. This avoids the colon problem.

        [task setEnvironment:[NSDictionary
dictionaryWithObject:newpath
                                                                                                         forKey:@"PATH"]];

        NSPipe *readPipe = [NSPipe pipe];
    NSFileHandle *readHandle = [readPipe
fileHandleForReading];

    NSPipe *writePipe = [NSPipe pipe];
    NSFileHandle *writeHandle = [writePipe
fileHandleForWriting];

    [task setStandardInput: writePipe];
    [task setStandardOutput: readPipe];

    [task launch];

    [writeHandle writeData:[NSData
dataWithContentsOfFile:@"/users/keithblount/Markdown/readme.markdown"]];
    [writeHandle closeFile];

    NSMutableData *data = [[NSMutableData alloc]
init];
    NSData *readData;

    while ((readData = [readHandle availableData])
           && [readData length]) {
        [data appendData: readData];
    }

Here you're writing all of the data in one shot, then you read all of the data back in. The problem is that this can produce a deadlock. Many processes read a little, write a little, read a little more, write a little more, etc. This may not be the case with yours, but it's possible, and should be guarded against. The problem is that if the subtask writes enough data (about 4k), it will block waiting for some data to be read. At which point it's no longer reading your data, so *you* will block, and you have deadlock.

If you're always going to be writing a file into the subtask, you can
simplify this by simply getting a file handle to it by using
+fileHandleForReadingAtPath:, and passing that file handle as the
task's standard input. Standard input doesn't have to be a pipe.

If you will eventually be writing in-app data, you'll have to get
fancier. Unfortunately, NSFileHandle doesn't even seem to have a
method for determining whether any data can be written, or for writing
only that data which would block, which makes it hard to implement
this correctly.

One possible method would be to ask the file handles for their
fileDescriptor, then use a standard UNIX select() loop to make sure
that you're reading and writing simultaneously. Another method would
be to spawn a temporary thread to do the writing.

    NSString *outputString;
    outputString = [[NSString alloc]
                         initWithData: data
                                                         encoding: NSASCIIStringEncoding];

This will fail if the data is non-ASCII. Which encoding is correct depends entirely on the subtask, however.

        //[data
writeToFile:@"/users/keithblount/Markdown/CocoaTest.tex"
atomically:YES];

    [task release];
    [data release];
    [outputString autorelease];

        [textView setString:outputString];      // Write the
string to the text view for testing purposes
}

For the sake of the archives, this was the original
shell script (named "md2latex.sh"):

cd "%SCR_BUNDLE_PATH"

MultiMarkdown.pl|SmartyPants.pl|xsltproc -novalid
-nonet "$SCR_BUNDLE_PATH/xhtml2memoir.xslt"  -

Another way to avoid farting around with paths would be to execute this script directly using a series of NSTasks. You can use NSPipes to get the same behavior as the | character in the script so that you don't have to write code to manage the IO between the tasks. Then again, that might be overcomplicating things.

Mike
_______________________________________________
Do not post admin requests to the list. They will be ignored.
Cocoa-dev mailing list      (email@hidden)
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden


References: 
 >Re: Using NSTask and NSPipe to perform a shell script (From: Spencer Salazar <email@hidden>)
 >Re: Using NSTask and NSPipe to perform a shell script (SOLVED) (From: Keith Blount <email@hidden>)

  • Prev by Date: Re: Processes vs. Threads in Cocoa software architectures
  • Next by Date: Re: Processes vs. Threads in Cocoa software architectures
  • Previous by thread: Re: Using NSTask and NSPipe to perform a shell script (SOLVED)
  • Next by thread: Re: Using NSTask and NSPipe to perform a shell script
  • Index(es):
    • Date
    • Thread