Показать статистику
0 голосов
от (2.5тыс. баллов)

Есть вопрос, который я не могу решить самостоятельно. Я написал скрипт для добавления каталога в среду PATH. Когда я запускаю скрипт, он работает нормально, и каталог добавляется в PATH. Но похоже, что изменение длится только до завершения сценария, а не на протяжении всего сеанса. Когда я смотрю на PATH после запуска скрипта,  то директории уже нет.

770 просмотров 1 ответов

1 Ответ

+1 голос
от (26.4тыс. баллов)

Важно помнить о 2-х таких вещах как:

  • Команды, включая сценарии, сохраняют свою среду на время выполнения команды
  • Команды наследуют среду от родительского процесса и  для команд, запускаемых через оболочку, они наследуются от оболочки.

Так что, если вы это сделаете

 PATH=$PATH:/my/dir

 то это продлится только в течение сценариев. Чтобы сделать его постоянным, родительская оболочка должна знать об изменениях. Правильный способ сделать это - написать

 ~/.bashrc

если вы используете bash или соответствующий rc-файл для вашей оболочки. Таким образом, мы можем использовать >> для добавления в файл

Сделайте вот так:

echo PATH=$PATH:/my/dir >> ~/.bashrc

И когда скрипт выйдет, запустите

source ~/.bashrc

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

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

Это определяет проблему. 

Вы можете манипулировать, используя простые переменные оболочки. 

$ foo=bar
$ echo $foo
bar
$ echo -e "foo=baz \n"'echo $foo' > script
$ cat script
foo=baz 
echo $foo
$ bash script
baz
$ echo $foo
bar

Хотя может и проще это делать при помощи подоболочек. На тот случай, если вы не хотите постоянно добавлять каталог в PATH, а только в текущий сеанс оболочки,  вам просто нужно запустить скрипт в текущей оболочке . Это делается с помощью source команды, которая сокращается до точки (.)

Например имеет такой сценарий

read -rp "What did you want to add to PATH? "
[ -d "$REPLY" ] && 
PATH="$PATH:$(readlink -m $REPLY)" &&
echo "OK, adding $REPLY to PATH" &&
echo "$PATH" ||
echo "seems like $REPLY is not a directory"

Но этот же результат можно получить если запускать скрипт обычным способом. Вот так

$ ./add-to-path
What did you want to add to PATH? /home/zanna/playground
OK, adding /home/zanna/playground to PATH
/home/zanna/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/home/zanna/playground
$ echo $PATH
/home/zanna/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games

Но когда применяем sourse, все работает как положено

$ . add-to-path
What did you want to add to PATH? /home/zanna/playground
OK, adding /home/zanna/playground to PATH
/home/zanna/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/home/zanna/playground
$ echo $PATH
/home/zanna/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/home/zanna/playground

Можно подвести итог.

  • Лучше  добавлять назначения PATH ~/.profileс чем  ~/.bashrc потому, что  ~/.bashrc поставляются каждой интерактивной оболочкой Bash, включая оболочки, запускаемые из текущей оболочки - это означает, что дочерние оболочки могут заканчиваться очень длинными PATH, поскольку они наследуют PATH, а также добавляются к нему, когда исходный текст ~/.bashrc, в отличие от этого ~/.profile обычно поступает только при входе в систему.
  • Присваивание PATH всегда будет наследоваться дочерними процессами,но не родительскими процессами, без явного export редактирования.
  • Проецирование переменных REPLYи PATH является хорошей идеей, поскольку  могут быть пробелы или другие символы, которые запускают расширения оболочки.
  • Недостатком является то, что ~  не расширяется, поэтому сценарий может возвращать, к примеру, такие вещи, как
looks like ~/some-existing-dir is not a directory
...