Понимание области действия и времени жизни переменных среды в сценариях оболочки

890
Decent Dabbler

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

Я пытаюсь выполнить скрипт PHP из TextWrangler, который в свою очередь открывает новый скрипт PHP с терминалом. (TextWrangler - это текстовый редактор, который может выполнять сценарии, расположенные в указанной папке, например, для работы с текущим активным документом).

Первый скрипт находится в:

/Users/<username>/Library/Application Support/TextWrangler/Scripts/ 

... и его содержание:

#!/usr/bin/php <?php var_dump( $_SERVER ); chdir( __DIR__ ); $file = realpath( '../Unix Support/test.php' ); exec( sprintf( 'open -a Terminal "%s" &', $file ) ); exit( 0 ); ?> 

Второй в:

/Users/<username>/Library/Application Support/TextWrangler/Unix Support/ 

... и его содержание:

#!/usr/bin/php <?php var_dump( $_SERVER ); exit( 0 ); ?> 

TextWrangler передает некоторые переменные окружения первому сценарию (через который я могу получить доступ $_SERVER), и они соответствуют ожиданиям. Например, правильный путь к файлу текущего документа, который активен в TextWrangler.

При первом запуске сценария переменные среды автоматически автоматически передаются и во второй сценарий (который я открываю exec()).

Теперь наступает расстраивающая часть: когда я переключаю активный документ в TextWrangler и снова запускаю сценарий, первый сценарий снова получает правильные переменные среды от TextWrangler, но второй сценарий все еще имеет старые переменные среды, если только я заранее не уничтожил Terminal. Очевидно, что сеанс терминала как-то запоминает первые переменные среды и не хочет обновляться.

Но кроме этого самого базового понимания, я не имею ни малейшего представления о том, как работают переменные среды, какова их область действия и т. Д. Итак, кто-то может объяснить, как я могу сделать это так (если возможно, для начала), что второй скрипт снова получает правильные переменные окружения, без необходимости каждый раз завершать работу терминала?

Я попытался явно установить переменные окружения в первом скрипте перед вызовом exec(), вот так:

foreach( $_SERVER as $key => $value ) { putenv( "$key=$value" ); } exec( ... etc. ); 

Я попытался сбросить переменные окружения в конце, как в первом, так и во втором скриптах, вот так:

foreach( $_SERVER as $key => $value ) { putenv( "$key" ); } 

Но ничего не работает, как я ожидаю. Любые новые идеи тщательно оценены.

редактировать:

Тем временем я нашел альтернативное, но неудовлетворительное решение: когда я вызываю open -n -a Terminal ...(обратите внимание на добавленный -nаргумент) с exec(), он каждый раз начинает новый сеанс терминала с правильными переменными среды. Но это каждый раз открывает совершенно новый экземпляр Terminal.

Обновить:

Я немного упростил процесс, используя только один сценарий вместо двух, тестируя переменную окружения SHLVL. Мне также удалось покончить с новым экземпляром Terminal, используя временный файл, как предложил Скотт. Это привело к следующему. Но я все еще чувствую, что это не очень элегантно.

$shellLevel = getenv( 'SHLVL' ); if( 1 == $shellLevel ) { file_put_contents( 'env.dat', serialize( $_SERVER ) ); exec( sprintf( 'open -a Terminal "%s" &', __FILE__ ) ); exit( 0 ); } else { $_SERVER = unserialize( file_get_contents( 'env.dat' ) ); unlink( 'env.dat' ); foreach( $_SERVER as $key => $value ) { putenv( "$key=$value" ); } } 

Так что я все еще открыты для других предложений (кроме CLI и файловых предложений, которые Скотт уже предлагал). Если, конечно, это просто невозможно (Скотт, казалось, уже намекал на это).

1

1 ответ на вопрос

1
Scott

Вы когда-нибудь использовали Microsoft Office (Word, Excel и PowerPoint)? Вы когда-нибудь открывали два документа / рабочих тетради / презентации одновременно? Вы заметили, что вы обычно получаете только один процесс соответствующего инструмента, даже если у вас есть два окна? Совершенно очевидно, что это или что-то похожее происходит с Терминалом - второй openзапрос обрабатывается процессом, который был создан первым open, и обрабатывается без создания нового процесса. Я думаю, openобнаруживает, что процесс уже существует, и просто отправляет ему сообщение, а не создает новый процесс или что-то в этом роде.

Что касается вашего другого вопроса: переменные среды передаются из родительского процесса в дочерний процесс (т. Е. При создании нового процесса fork) и сохраняются при execвызовах. Таким образом, они идут вниз по иерархии процессов, пока процесс не выйдет или не изменит явно переменную. Таким образом, похоже, что первая openвызывает a forkи a execтерминала, тем самым пропуская среду, тогда как вторая openпросто отправляет сообщение существующему процессу для ../Unix Support/test.phpповторного запуска, и среда не передается. Похоже, вы нашли единственные способы реализовать желаемую функциональность с помощью среды - каждый раз форсировать создание нового процесса. Другие подходы включают передачу необходимых данных другими способами, такими как командная строка или файл.

Скотт, спасибо. Ваше последнее предложение - то, что я в конечном итоге делал в то же время, на самом деле. Теперь я упростил задачу, используя только один сценарий, протестировав передаваемую переменную окружения SHLVL, чтобы узнать, является ли сценарий родительским или дочерним, и действительно сохранив среду во временном файле, если он является родительским и читающим, и `putenv ()` -ing их, если это дочерний скрипт. Но это не очень элегантно. Вы уверены, что нет более элегантного способа обновления переменных среды для того же сеанса терминала, кроме cli или file? Decent Dabbler 11 лет назад 0

Похожие вопросы