Узнать расположение исполняемого файла в Какао - PullRequest
6 голосов
/ 16 октября 2008

Простой вопрос: как узнать расположение исполняемого файла в приложении Какао.

Помните, что во многих Unix-подобных ОС люди используют среду PATH для назначения предпочтительного местоположения для своих исполняемых файлов, особенно когда в их системе есть несколько версий одного и того же приложения. Хорошей практикой является то, что наше приложение Какао должно найти ПРЕДПОЧТИТЕЛЬНОЕ расположение нужного исполняемого файла.

Например, SVN 1.4 в конфигурации Leopard по умолчанию был в / usr / bin, и вы установили гораздо более новую версию, скажем SVN 1.5.3, через MacPorts в / opt / local / bin. И вы устанавливаете свой PATH, используя /etc/path.d или .bash_profile или .zshrc, вот так:

экспорт PATH = / opt / local / bin: $ PATH

Таким образом, вы можете использовать новую версию svn вместо старой из системы. Хорошо работает в любой терминальной среде. Но не в приложениях Какао. Какао-приложение, насколько мне известно, имеет только стандартную среду PATH, например:

export PATH = "/ usr / bin: / bin: / usr / sbin: / sbin"

По умолчанию он не будет использовать конфигурацию в /etc/path.d, .bash_profile, .profile, .zshrc и т. Д.

Так как именно мы можем это сделать?

p.s. У нас есть полу-решение здесь , но оно не может полностью удовлетворить цель этого вопроса.

Ответы [ 5 ]

9 голосов
/ 16 октября 2008

Сложная попытка сделать это состоит в том, что пользователь может установить для своей оболочки что угодно: sh, bash, csh, tcsh и т. Д., И каждая оболочка по-разному настраивает свою терминальную среду. Я не уверен, что сам пойду на неприятности из-за этого, но если вы действительно хотите, вот мой путь.

Первым шагом является выяснение оболочки пользователя. В OS X эта информация хранится в службах каталогов, к которым можно получить доступ через API-интерфейсы в DirectoryService.framework или с помощью инструмента командной строки dscl. API DirectoryService - огромная боль в заднице, поэтому я бы, вероятно, пошел по пути CLI. В Какао вы можете использовать NSTask для запуска инструмента с аргументами, чтобы получить оболочку пользователя (подробности об этом я оставлю в другом месте). Команда будет выглядеть примерно так:

dscl -plist localhost -read /Local/Default/Users/username UserShell

Это вернет XML-текст, который вы можете интерпретировать как plist и преобразовать в NSDictionary, или вы можете опустить опцию -plist и проанализировать текстовый вывод самостоятельно.

Как только вы узнаете путь к пользовательской оболочке, следующим шагом будет выполнение этой оболочки и указание ей выполнить команду env для распечатки пользовательской среды. Похоже, что большинство оболочек принимает опцию командной строки -c, которая позволяет передавать строку для выполнения - я думаю, вам просто нужно будет предположить, что это общий интерфейс для любой оболочки, выбранной пользователем.

Когда у вас есть пользовательская среда, вы можете извлечь из нее список путей и выполнить поиск любого исполняемого файла, который вы ищете из этого. Как я уже сказал, я действительно не знаю, стоит ли это проблем, но я бы пошел в этом направлении, если бы реализовывал это.

8 голосов
/ 23 декабря 2008

Относится к ответу Брайана Вебстера:

Более простой способ получить оболочку пользователя - использовать класс NSProcessInfo. * например 1003 *

NSDictionary *environmentDict = [[NSProcessInfo processInfo] environment];
NSString *shellString = [environmentDict objectForKey:@"SHELL"];

Что проще, чем использовать dscl и анализировать ввод XML.

6 голосов
/ 11 ноября 2011

Вот моя реализация, основанная на ответах выше, которая будет вызвана из applicationDidFinishLaunching:

// from http://cocoawithlove.com/2009/05/invoking-other-processes-in-cocoa.html
#import "NSTask+OneLineTasksWithOutput.h"

void FixUnixPath() {
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.1 * NSEC_PER_SEC), dispatch_get_main_queue(), ^(void){
        NSString *userShell = [[[NSProcessInfo processInfo] environment] objectForKey:@"SHELL"];
        NSLog(@"User's shell is %@", userShell);

        // avoid executing stuff like /sbin/nologin as a shell
        BOOL isValidShell = NO;
        for (NSString *validShell in [[NSString stringWithContentsOfFile:@"/etc/shells" encoding:NSUTF8StringEncoding error:nil] componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]]) {
            if ([[validShell stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] isEqualToString:userShell]) {
                isValidShell = YES;
                break;
            }
        }

        if (!isValidShell) {
            NSLog(@"Shell %@ is not in /etc/shells, won't continue.", userShell);
            return;
        }
        NSString *userPath = [[NSTask stringByLaunchingPath:userShell withArguments:[NSArray arrayWithObjects:@"-c", @"echo $PATH", nil] error:nil] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
        if (userPath.length > 0 && [userPath rangeOfString:@":"].length > 0 && [userPath rangeOfString:@"/usr/bin"].length > 0) {
            // BINGO!
            NSLog(@"User's PATH as reported by %@ is %@", userShell, userPath);
            setenv("PATH", [userPath fileSystemRepresentation], 1);
        }
    });
}

P.S. Причина, по которой это работает, заключается в том, что он улавливает изменения среды, сделанные оболочкой. Например. RVM добавляет PATH=$PATH:$HOME/.rvm/bin к .bashrc при установке. Приложения какао запускаются из launchd, поэтому они не имеют этих изменений в своем PATH.

Я не на 100% доволен этим кодом, потому что он не ловит все. Моим первоначальным намерением было специально работать с RVM, поэтому мне пришлось использовать оболочку без входа в систему, но на практике люди случайным образом помещают модификацию PATH в .bashrc и .bash_profile, так что было бы лучше запустить оба.

У одного из моих пользователей даже было интерактивное меню (!!!) в его профиле оболочки, что, естественно, привело к зависанию этого кода, и я экспортировал флаг оболочки env только для него. :-) Добавление таймаута, вероятно, хорошая идея.

Это также предполагает, что оболочка совместима с bourne и поэтому не работает с fish 2.0, который становится все более популярным среди сообщества хакеров. (Fish считает $ PATH массивом, а не строкой, разделенной двоеточиями. И, таким образом, по умолчанию он печатает ее, используя пробелы в качестве разделителей. Вероятно, можно создать простое исправление, например, запустить for i in $PATH; echo "PATH=$i"; end, а затем взять только строки, начинающиеся с PATH=. Фильтрация - хорошая идея в любом случае, потому что скрипты профиля часто печатают что-то самостоятельно.)

В заключение следует отметить, что этот код является важной частью приложения для доставки уже более года (10 лучших платных инструментов для разработчиков в Mac App Store большую часть года). Тем не менее, я сейчас реализую песочницу и убираю ее; естественно, вы не можете сделать этот трюк из изолированного приложения. Я заменяю его явной поддержкой RVM и друзей и вручную воспроизводю их соответствующие изменения в env.

Для тех, кто хочет использовать что-то вроде системного Git из изолированного приложения, обратите внимание, что, хотя у вас нет доступа для чтения файлов и перечисления каталогов, у вас есть доступ к stat - [[NSFileManager defaultManager] fileExistsAtPath:path]. Вы можете использовать это, чтобы проверить жестко запрограммированный список типичных папок, ищущих ваш бинарный файл, и когда вы найдете места (например, / usr / local или / opt / local или что-то еще), попросите пользователя предоставить вам доступ через NSOpenPanel. Это не охватит каждый случай, но будет обрабатывать 90% случаев использования, и это лучшее, что вы можете сделать для своих пользователей из коробки.

2 голосов
/ 23 декабря 2008

Какова вероятность того, что ваши пользователи будут иметь пользовательские версии используемого вами инструмента (и насколько вероятно, что ваше приложение совместимо с произвольными версиями инструмента)? Если ответ «не очень», рассмотрите возможность использования пути к предоставленному системой инструменту по умолчанию и предоставления опытным пользователям возможности указать собственный путь в качестве предпочтения.

1 голос
/ 16 октября 2008

Не указан ли путь для Finder (и, следовательно, для любых запущенных приложений с графическим интерфейсом Cocoa) из вашей оболочки входа в систему? Если ваша оболочка входа в систему и оболочка, которую вы используете в Terminal.app, не совпадают, это, вероятно, приведет к некоторой путанице.

Эта информация может быть полезна: http://lists.apple.com/archives/cocoa-dev/2005/Oct/msg00528.html

Очевидно, что "правильный" способ установки переменных среды для процессов графического интерфейса пользователя - это скрытый файл .plist. Я уверен, что когда-то знал это, а потом быстро забыл.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...