NSTask有多个管道输入

时间:2012-04-19 02:39:45

标签: objective-c cocoa nstask nspipe

我正在尝试使用管道来处理需要多个输入的命令,但不太确定如何操作。这是我正在尝试做的事情的片段。我知道如何处理第一个输入,但我迷失了第二个输入-newstdinpass

NSTask *task = [[NSTask alloc] init];
NSPipe *pipe = [NSPipe pipe];

[task setLaunchPath: @"/bin/sh"];
[task setArguments: [NSArray arrayWithObjects: @"-c", @"/usr/bin/hdiutil chpass -oldstdinpass -newstdinpass /path/to/dmg", nil]];
[task setStandardInput:pipe];
[task launch];

[[pipe fileHandleForWriting] writeData:[@"thepassword" dataUsingEncoding:NSUTF8StringEncoding]];
[[pipe fileHandleForWriting] closeFile];

[task waitUntilExit];
[task release];

所以我知道以这种方式使用hdiutil有点像黑客,但就管道方面而言,我是否采用了正确的方式?

感谢。

UPDATE :如果其他人对此感到疑惑,我的问题的一个快速解决方案是传入一个以null为终结的字符串,正如Ken Thomases在下面指出的那样。将[[NSString stringWithFormat:@"oldpass\0newpass\0"] dataUsingEncoding:NSUTF8StringEncoding]用于管道。现在,仍然需要学习如何使用管道桥接多个NSTasks ...

2 个答案:

答案 0 :(得分:3)

您可以创建多个NSTask和一堆NSPipe并将它们连接在一起,或者您可以使用sh -c技巧为shell提供命令,并让它解析它并建立了所有的IPC。

示例:

NSTask *task;
task = [[NSTask alloc] init];
[task setLaunchPath: @"/bin/sh"];

NSArray *arguments;
arguments = [NSArray arrayWithObjects: @"-c",
                     @"cat /usr/share/dict/words | grep -i ham | rev | tail -5", nil];
[task setArguments: arguments];
// and then do all the other jazz for running an NSTask.

参考: http://borkware.com/quickies/one?topic=nstask


现在,至于“正确的”命令执行功能,这是我通常使用的一个......

代码:

/*******************************************************
 *
 * MAIN ROUTINE
 *
 *******************************************************/

- (void)runCommand:(NSString *)cmd withArgs:(NSArray *)argsArray
{
    //-------------------------------
    // Set up Task
    //-------------------------------

    if (task) { [self terminate]; }

    task = [[NSTask alloc] init];
    [task setLaunchPath:cmd];
    [task setArguments:argsArray];

    [task setStandardOutput:[NSPipe pipe]];
    [task setStandardError:[task standardOutput]];

    //-------------------------------
    // Set up Observers
    //-------------------------------

    [PP_NOTIFIER removeObserver:self];
    [PP_NOTIFIER addObserver:self 
                    selector:@selector(commandSentData:) 
                        name: NSFileHandleReadCompletionNotification 
                      object: [[task standardOutput] fileHandleForReading]];

    [PP_NOTIFIER addObserver:self 
                    selector:@selector(taskTerminated:) 
                        name:NSTaskDidTerminateNotification 
                      object:nil];

    //-------------------------------
    // Launch
    //-------------------------------
    [[[task standardOutput] fileHandleForReading] readInBackgroundAndNotify];

    [task launch];
}

/*******************************************************
 *
 * OBSERVERS
 *
 *******************************************************/

- (void)commandSentData:(NSNotification*)n
{
    NSData* d;
    d = [[n userInfo] valueForKey:NSFileHandleNotificationDataItem];

    if ([d length])
    {
        NSString* s = [[NSString alloc] initWithData:d encoding:NSUTF8StringEncoding];

        NSLog(@"Received : %@",s);
    }

    [[n object] readInBackgroundAndNotify]; 
}

- (void)taskTerminated:(NSNotification*)n
{
    [task release];
    task = nil;
}

答案 1 :(得分:2)

您对管道的使用对我来说是正确的。

我不确定你为什么要使用/bin/sh。只需设置NSTask,其发布路径为@"/usr/bin/hdiutil",其参数为@"chpass"@"-oldstdinpass"@"-newstdinpass"和{{1}的数组}。这更安全。例如,如果dmg的路径包含shell将解释的字符,如@"/path/to/dmg"

,该怎么办?

除非您明确希望利用shell功能,否则请不要使用shell。