Как использовать NSTask для запуска команд терминала в цикле в согласованной среде?
Я хочу использовать NSTask для симуляции терминала для запуска команд. Коды следующие. Он может получать входные данные в цикле и возвращать выходные данные процесса.
int main(int argc, const char * argv[])
{
@autoreleasepool {
while (1) {
char str[80] = {0};
scanf("%s", str);
NSString *cmdstr = [NSString stringWithUTF8String:str];
NSTask *task = [NSTask new];
[task setLaunchPath:@"/bin/sh"];
[task setArguments:[NSArray arrayWithObjects:@"-c", cmdstr, nil]];
NSPipe *pipe = [NSPipe pipe];
[task setStandardOutput:pipe];
[task launch];
NSData *data = [[pipe fileHandleForReading] readDataToEndOfFile];
[task waitUntilExit];
NSString *string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(@"%@", string);
}
}
Мой вопрос: когда цикл заканчивается, работающая среда восстанавливается до состояния инициализации. Например, рабочий путь по умолчанию /Users/apple
и я бегу cd /
изменить путь к /
, а затем запустить pwd
Верни /Users/apple
а не /
,
Так как я могу использовать NSTask
полностью смоделировать Терминал?
1 ответ
cd
а также pwd
являются встроенными командами оболочки Если вы выполните задачу
/bin/sh -c "cd /"
нет способа вернуть измененный рабочий каталог обратно в вызывающий процесс. Та же проблема существует, если вы хотите установить переменные MYVAR=myvalue
,
Вы можете попытаться разобрать эти строки отдельно и обновить среду. Но как насчет многострочных команд, таких как
for file in *.txt
do
echo $file
done
Вы не можете подражать этому, посылая каждую строку в отдельности NSTask
процессы.
Единственное, что вы можете сделать, это начать сингл /bin/sh
процесс с NSTask
и подать все входные строки на стандартный ввод этого процесса. Но тогда вы не можете использовать readDataToEndOfFile
читать вывод, но вы должны читать асинхронно (используя [[pipe fileHandleForReading] waitForDataInBackgroundAndNotify]
).
Короче говоря: вы можете смоделировать Терминал, только запустив (одну) оболочку.
ДОБАВЛЕНО: Возможно, вы можете использовать следующее в качестве отправной точки для вашего приложения. (Я пропустил все проверки ошибок.)
int main(int argc, const char * argv[])
{
@autoreleasepool {
// Commands are read from standard input:
NSFileHandle *input = [NSFileHandle fileHandleWithStandardInput];
NSPipe *inPipe = [NSPipe new]; // pipe for shell input
NSPipe *outPipe = [NSPipe new]; // pipe for shell output
NSTask *task = [NSTask new];
[task setLaunchPath:@"/bin/sh"];
[task setStandardInput:inPipe];
[task setStandardOutput:outPipe];
[task launch];
// Wait for standard input ...
[input waitForDataInBackgroundAndNotify];
// ... and wait for shell output.
[[outPipe fileHandleForReading] waitForDataInBackgroundAndNotify];
// Wait asynchronously for standard input.
// The block is executed as soon as some data is available on standard input.
[[NSNotificationCenter defaultCenter] addObserverForName:NSFileHandleDataAvailableNotification
object:input queue:nil
usingBlock:^(NSNotification *note)
{
NSData *inData = [input availableData];
if ([inData length] == 0) {
// EOF on standard input.
[[inPipe fileHandleForWriting] closeFile];
} else {
// Read from standard input and write to shell input pipe.
[[inPipe fileHandleForWriting] writeData:inData];
// Continue waiting for standard input.
[input waitForDataInBackgroundAndNotify];
}
}];
// Wait asynchronously for shell output.
// The block is executed as soon as some data is available on the shell output pipe.
[[NSNotificationCenter defaultCenter] addObserverForName:NSFileHandleDataAvailableNotification
object:[outPipe fileHandleForReading] queue:nil
usingBlock:^(NSNotification *note)
{
// Read from shell output
NSData *outData = [[outPipe fileHandleForReading] availableData];
NSString *outStr = [[NSString alloc] initWithData:outData encoding:NSUTF8StringEncoding];
NSLog(@"output: %@", outStr);
// Continue waiting for shell output.
[[outPipe fileHandleForReading] waitForDataInBackgroundAndNotify];
}];
[task waitUntilExit];
}
return 0;
}