Понимание переменных окружающей среды в разных контекстах

Попытка понять поведение среды в Linux (конкретно Ubuntu 13.04), я нахожу различные ситуации, когда настройка переменных envirionment используется или определяется для / в разных контекстах. Например, если я проверяю, locale , я получаю:

 $ locale LANG=en_US.UTF-8 LANGUAGE=es_ES:es_HN:es_EC:en LC_CTYPE="en_US.UTF-8" LC_NUMERIC=es_ES.UTF-8 // more output 

Но, если я нахожу, например, LC_CTYPE используя env | grep "LC_CTYPE" env | grep "LC_CTYPE" , он не выводит результат. В общем, locale показывает мне 13 LC_* переменных и env только девять:

 $ locale | grep "LC_*" | wc -l 13 $ env | grep "LC_*" | wc -l 9 

Другой переменной с различной «природой» является PS1 . Например:

 $ env | grep "PS1" # No output, but... $ set | grep "PS1" | head -n 1 PS1=$'\\[\\033[1;33m\\][\\t][\\W]\342\230\233\\[\\033[0m\\] ' 

и, конечно, PS1 – это четко определенная переменная в моей текущей среде, так как я вижу, что моя подсказка изменилась соответствующим образом.

Другой способ просмотра переменных среды в другом контексте – с помощью strace . Это программа, которая позволяет вам видеть, что происходит при выполнении программы. Образец:

 $ strace -v ./a.out # a.out is a common Hello World, made in C. execve("./a.out", ["./a.out"], ["LC_PAPER=es_ES.UTF-8", ...]) = 0 brk(0) access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory) # etc, etc write(1, "Hello World\n", 12Hello World ) = 12 exit_group(0) = ? 

Первое, что делает оболочка при выполнении программы, – это вызов execve , который действительно вызывает программу. Его первым аргументом называется вызываемая программа, вторая – параметры argv вызываемой программы, а третий параметр – переменные среды.

В этом третьем параметре, например, не отображается ни PS1 ни LC_TYPE .

В общем случае переменные, появляющиеся в env или set появляются в списке переменных окружения, отправленных в execve . Некоторые переменные locale отображаются в env или set но не в других ( LC_TYPE , LC_COLLATE и LC_MESSAGE , а также LC_ALL но с пустым значением). Наконец, другие переменные не определены в env хотя они имеют видимый эффект ( PS1 ), что отражается в set .

Что тут происходит? Каковы различия между env , set (без аргументов), locale (очевидно, только для переменных языка)?

  • Является ли `ls -l` сортировка файлов неинтуитивно?
  • установить LC_ *, но не LC_ALL
  • Каков порядок сортировки Linux по умолчанию?
  • Изменить язык GDM
  • Как установить языковой стандарт cs_CZ.ISO8859-2 на Ubuntu 12.04
  • Как изменить язык Firefox?
  • zsh сломал ключ и специальные символы
  • Chromebook Crouton Debian xfce4 - Невозможно изменить язык!
  • 3 Solutions collect form web for “Понимание переменных окружающей среды в разных контекстах”

    Основная проблема здесь – которая объясняет, почему, например, $PS1 не сообщается env – это то, что env сообщает из неинтерактивной среды. Процессы выполняются из вилки вашей интерактивной оболочки, но есть тонкость, связанная с тем, как их среда установлена: она фактически унаследована через встроенную внешнюю переменную уровня С, установленную для всех процессов exec() 'd (см. man environ ). Вот иллюстрация:

     #include <stdio.h> extern char **environ; int main (void) { int i; for (i = 0; environ[i] != NULL; i++) { printf("%s\n", environ[i]); } return 0; } 

    Что интересного в этом, если вы его компилируете и запускаете, вы обнаружите, что содержимое **environ точно соответствует тому, которое было сообщено env :

     $ gcc test.c $ ./a.out > aout.txt $ env > env.txt $ diff env.txt aout.txt 68c68 < _=/bin/env --- > _=./a.out 

    Единственное отличие – это имя исполняемого файла. Итак, откуда происходит **environ и почему она не содержит, например, $PS1 ?

    Основное объяснение заключается в том, что процесс всегда создается как дети других процессов, и они наследуют **environ , но PS1 никогда не был частью этого. При запуске оболочка может передавать исходные переменные из стандартных мест, и эти места различаются в зависимости от того, является ли оболочка интерактивной или нет; см. INVOCATION in man bash . Одним из аспектов этого является то, что:

    PS1 устанавливается […], если bash является интерактивным, позволяя сценарию оболочки или загрузочному файлу проверять это состояние.

    Теперь заметьте в /etc/bashrc что-то вроде этого:

     # are we an interactive shell? if [ "$PS1" ]; then 

    Именно там задано ваше фактическое (фантастическое) приглашение, и ни оно, ни начальное значение $PS1 были export . Начальное значение было создано оболочкой при вызове, потому что оно было интерактивным, а затем было получено то, что этот файл, но PS1 не попал в **environ . Вы можете увидеть это, если вы выполните:

     #!/bin/sh echo $PS1 

    Ничего – даже если вы echo $PS1 в своей интерактивной оболочке, это определено. Это связано с тем, что **environ выполненного #!/bin/sh является таким же, как и у родительской интерактивной оболочки, но не содержит PS1 . Это подразумевает, что каждая оболочка использует внутреннюю таблицу глобальных переменных отдельно, но изначально заселенную, из **environ (это запутывает, так как это означает, что **environ не включает много вещей, называемых переменными среды ).

    Содержимое **environ находится в /proc/[PID]/environ , и если вы проверите, что для вашей текущей интерактивной оболочки cat /proc/$BASHPID/environ вы увидите, что PS1 там нет.

    Но как материал попадает в «окружающую среду»?

    Простой ответ – через системные вызовы. Например, если мы перебросим некоторые вещи в пример программы C из более ранних версий:

     #include <errno.h> #include <stdio.h> #include <stdlib.h> #include <string.h> extern char **environ; int main (void) { int i; if (putenv("MYFOO=whatbar?")) { fprintf(stderr, "putenv() failed: %s\n", strerror(errno)); exit(1); } for (i = 0; environ[i] != NULL; i++) { printf("%s\n", environ[i]); } return 0; } 

    MYFOO=whatbar? будет отображаться на выходе (см. man putenv ). Поскольку оболочка создает процессы fork() ing (которая дублирует стек памяти родителя), а затем вызывает execv() (который проходит по дублированному **environ ), мы можем видеть механизм, посредством которого переменные среды могут export в дочерние процессы.

    Если вы выкинете fork() в этот пример, вы увидите, что это так, и (чтобы повторить) этот процесс fork'ing и потенциально exec'ing – это то, как создаются дочерние процессы и наследуют **environ от их предки. exec вызовы заменяют образ процесса, но в соответствии с man execv и man execv (некоторые версии первого не относятся к этому), **environ передается системой.

    Вот буквальная версия и exec /usr/bin/env с MYFOO=whatbar? экспортируется через putenv() :

     #include <errno.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> extern char **environ; int main (void) { pid_t pid; if (putenv("MYFOO=whatbar?")) { fprintf(stderr, "putenv() failed: %s\n", strerror(errno)); exit(1); } pid_t pid = fork(); if (!pid) execl("/usr/bin/env", "env", NULL); return 0; } 

    Итак, где же материал, который не находится в «окружающей среде»?

    Это частные данные конкретного экземпляра оболочки. Bash покажет вам это + унаследованный материал среды через set без аргументов. Обратите внимание, что этот вывод также включает в себя исходные функции.

    Но, если я нахожу, например, LC_CTYPE, используя env | grep "LC_CTYPE", он не выводит результат. В общем, locale показывает мне 13 LC_ * переменных и env только девять:

    Я не получаю никаких переменных LC_ от env (только LANG ), кроме 13 из locale . Я бы предположил, что это переменные, заданные locale вызовом, а не экспортированные; тот факт, что вы получаете какой-либо из env возможно, отражает наивную ошибку в некоторой конфигурации где-то.

    Оболочка знает два типа переменных:

    1. «внутренние» переменные, которые известны только оболочке (и к подоболочкам)

    2. экспортируемые переменные, «официальные», которые видны посредством execve и, следовательно, env . В export оболочке отображаются экспортированные переменные.

    Если вы выполните

     export PS1 

    и повторить

     env | grep "PS1" 

    то вы это видите. Переменные можно экспортировать во время создания ( export foo=bar вместо foo=bar ), они могут быть экспортированы автоматически при создании или изменении ( set -a ), их можно экспортировать позже ( var=foo; ...; export var ) и они могут быть «не export -n var » ( export -n var ).

    Если оболочка создает «настоящие» подоболочки (посредством a|b , (a;b) , $(a) и т. Д.), Она удерживает несколько неэкспортированных переменных во избежание хаоса.

    Результат команды locale не является списком переменных среды из текущей среды. Это отображение эффективных параметров локали (которые частично зависят от определенных переменных среды) и представлено в том же key=value формате, что и команда env .

    Вы можете увидеть источник для реализации команды eglibc locale : http://www.eglibc.org/cgi-bin/viewvc.cgi/branches/eglibc-2_19/libc/locale/programs/locale.c?view=markup

    Linux и Unix - лучшая ОС в мире.