2012-04-19 106 views
3

我想使用管道來處理需要多個輸入的命令,但不太清楚如何去做。這是我想要做的一個片段。我知道如何處理第一個輸入,但我在第二輸入-newstdinpassNSTask與多個管道輸入

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與管...

回答

3

您可以創建多個NSTask S和一堆NSPipe S和鉤在一起,或可以使用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

感謝您的代碼。對於如何將同一個命令的多個任務組合在一起,我仍然有點困惑,但是我會對它採取一些措施。 – Daniel 2012-04-19 05:58:11

+1

使用一個管道作爲一個任務的標準輸出和下一個任務的標準輸入。然後另一個管道將第二個任務的輸出連接到第三個任務的輸入。等等。如果你想爲整個鏈提供輸入,建立一個管道並將其設置爲第一個任務的輸入,並按照你的要求寫入。如果你想從整個鏈中輸出,建立一個管道並將其設置爲鏈中最後一個任務的標準輸出並從中讀取。 – 2012-04-20 08:58:46

+1

你已經保存了一天 – 2017-12-15 01:47:19

2

您對管道的使用看起來是正確的。我不確定你爲什麼使用/bin/sh。只需設置NSTask,其發射路徑爲@"/usr/bin/hdiutil",其參數爲@"chpass",@"-oldstdinpass",@"-newstdinpass"@"/path/to/dmg"的陣列。這更安全。例如,如果dmg的路徑包含shell將解釋的字符,例如$

除非你明確想要利用shell功能,不要使用shell。

+0

這是一個很好的建議,但它仍然沒有解決如何通過單一的stdin管道發送'-oldstpasspass'值和'-newstdinpass'值。 – 2012-04-19 04:09:25

+0

@Ken,這是很好的建議。我實際上有一些奇怪的問題,因爲'NSTask'不能正確地將啓動路徑設置爲'@「/ usr/bin/hdiutil」'並將'create'動詞作爲參數傳入。我會嘗試非shell的方式。 – Daniel 2012-04-19 05:49:19

+2

@RobKeniger,我沒有真正看過'hdiutil'命令的細節以及它如何獲取密碼。它聲稱將它們按順序排列,以空終止(不是換行符終止)。因此,在關閉它之前在管道上寫入兩者是可行的。 – 2012-04-19 14:04:18