NSTask выполнить команду поиска с аргументами

Я пытаюсь выполнить команду терминала из NSTask, но мне кажется, что я делаю что-то не так.

Я пробовал это:

  NSTask *task = [[NSTask alloc] init];
task.launchPath = @"/bin/bash";

NSString *arr = [NSString stringWithFormat:@"find %@ -type f -name \"*.m\" -exec sed -i '' \"s/NSLog/\\/\\/NSLog/\" {} +",path];
task.arguments = [arr componentsSeparatedByString:@" "];

[task launch];

с множеством вариаций аргументов, но я не могу заставить его работать. Команда терминала работает нормально:

find /a/path/ -type f -name '*.m' -exec sed -i '' s/NSLog/\\/\\/NSLog/ {} +

person BlackM    schedule 27.02.2016    source источник
comment
task.arguments дает правильный ответ?   -  person matt    schedule 27.02.2016


Ответы (1)


Если вы собираетесь использовать /bin/bash в качестве пути запуска задачи, тогда вам нужно использовать список аргументов, который будет работать, если вы вызовете /bin/bash в качестве команды, а не команду, как вы бы вводили как input в /bin/bash командная строка. Это разные вещи.

То есть ваш код выполняет в оболочке примерно эквивалент следующей команды:

/bin/bash find some path here -type f -name "*.m" -exec sed -i '' "s/NSLog/\/\/NSLog/" {} +

Обратите внимание на /bin/bash в начале этой команды. Также обратите внимание, что это не сработает. Наконец, я намеренно написал путь как «некоторый путь здесь» с пробелами, чтобы подчеркнуть, что ваша команда не предусматривает, что путь, в котором есть пробелы, будет рассматриваться как единственный аргумент для find.

Если вы хотите запустить /bin/bash и передать ему строку для интерпретации как команду, вам нужно использовать параметр -c и передать строку команды в качестве единственного аргумента для этой опции -c. Так:

/bin/bash -c 'find some path here -type f -name "*.m" -exec sed -i "" "s/NSLog/\/\/NSLog/" {} +'

Это решает одну из проблем. Это будет соответствовать:

task.arguments = @[ @"-c", arr ];

Это все еще не решает проблему с путем. Вы можете наивно попытаться решить эту проблему, изменив строку формата, заключив в кавычки спецификатор формата %@, например:

NSString *arr = [NSString stringWithFormat:@"find \"%@\" -type f -name \"*.m\" -exec sed -i '' \"s/NSLog/\\/\\/NSLog/\" {} +",path];

Однако это не помогает, если в пути есть кавычки. Хуже того, это не помогает, если в пути есть специальные символы, такие как $ или `. Путь может быть интерпретирован оболочкой и выполнением подкоманд. Например, путь, в который входит $(rm -rf ~), будет катастрофическим.

Вы можете попробовать процитировать его в одинарные кавычки:

NSString *arr = [NSString stringWithFormat:@"find '%@' -type f -name \"*.m\" -exec sed -i '' \"s/NSLog/\\/\\/NSLog/\" {} +",path];

Но сам путь может включать одиночную кавычку, которая завершает цитирование, а затем специальные символы. Вредоносный путь просто меняется на '$(rm -rf ~).

Можно правильно указать путь в кавычки, но в целом глупо использовать оболочку, когда в этом нет необходимости.

Намного лучше напрямую вызвать исполняемый файл, который вы хотите запустить (/usr/bin/find), и передать ему аргументы. Здесь нет оболочки, поэтому нет риска интерпретации каких-либо аргументов оболочкой.

So:

task.launchPath = @"/usr/bin/find";
task.arguments = @[ path, @"-type", @"f", @"-name", @"*.m", @"-exec", @"sed", @"-i", @"", @"s/NSLog/\\/\\/NSLog/\", @"{}", @"+" ];

Еще лучше выполнить перечисление каталогов - то, что вы здесь используете find - в коде, поскольку это просто.

NSURL* url = [NSURL fileURLWithPath:path];
NSFileManager* fm = [[NSFileMananger alloc] init];
// Consider passing NSDirectoryEnumerationSkipsPackageDescendants in the options
NSDirectoryEnumerator* e = [fm enumeratorAtURL:url includingPropertiesForKeys:nil options:0 errorHandler:nil];
for (NSURL* item in e)
{
    if ([item.pathExtension isEqualToString:@"m"])
    {
        // Use NSTask to run your /usr/bin/sed command on the item
    }
}

Наконец, похоже, что команда, которую вы передаете sed, не может работать. Может быть, ему нужен другой уровень экранирования (против компилятора C и самого sed), но проще просто не рассматривать косую черту как специальный символ. Вы можете и должны использовать другой разделитель вокруг шаблона, если хотите использовать косую черту в замене. Например: s!NSLog!//NSLog!.

person Ken Thomases    schedule 27.02.2016
comment
ОТЛИЧНЫЙ ответ, который помог мне многое понять о команде find в Unix. Большое спасибо! - person BlackM; 27.02.2016