Переменные окружения в linux

Примеры эмулируемых терминалов

Многие эмуляторы терминалов были разработаны для терминалов, таких как VT52 , VT100 , VT220 , VT320 , IBM 3270/8/9 / E , IBM 5250 , IBM 3179G , Data General D211, Hewlett Packard HP700 / 92, Sperry / Unisys серии 2000 UTS60. Берроуз / Unisys A-серии T27 / TD830 / ET1100, ADDS ViewPoint, ВС консоли, QNX , AT386, SCO-ANSI, SNI 97801, Televideo и Wyse 50/60. Кроме того, были разработаны программы для эмуляции других эмуляторов терминала, таких как xterm и различные консольные терминалы (например, для Linux ). Наконец, некоторые эмуляторы просто ссылаются на стандарт, например ANSI . Такие программы доступны на многих платформах, от DOS и Unix до Windows и macOS, до встроенных операционных систем, используемых в мобильных телефонах и промышленном оборудовании.

Установка переменных окружения

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

Для создания переменной окружения экспортируем нашу недавно созданную переменную оболочки:

Проверяем результат, действительно ли мы создали переменную окружения:

На этот раз, если вы попытаетесь отобразить переменную в новой оболочке, получите её значение:

Результат:

Вы также можете использовать и следующую конструкцию для создания переменной окружения:

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

Для чего нужно виртуальные среды?

При создании скрипта или программы вы часто используете сторонние модули (пакеты). Если в последующем потребуется перенос программы на другой компьютер, то вы можете столкнуться с двумя проблемами:

  1. Нужные пакеты отсутствуют на новом компьютере и придется проверять каждый файл программы для их поиска. Либо получить список установленных пакетов через «pip list» на старом компьютере, который выведет множество лишних модулей.
  2. Даже если количество пакетов или файлов программ маленькое, или вы его создали отдельно, то вы все равно можете столкнуться с проблемами в версиях. Пакеты могли быть обновлены, а методы и классы изменены.

Использование виртуальных сред избавляет вас от этих проблем. В таком виртуальной среде находится свой интерпретатор, свой pip и все пакеты относятся только к нему. Так же, весь проект, можно перенести как папку на другой компьютер без ошибок.

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

Виды переменных окружения

Как уже отмечалось, в системе существуют, образно выражаясь, различные рабочие среды для разных уровней доступа и продолжительности действия переменных. Для организации работы пользователей действует пользовательская среда (окружение), для корректного функционирования компонентов системы — системное окружение, доступное только суперпользователю. В процессе работы также можно устанавливать временные переменные окружения, доступные текущему пользователю и только на время действия текущего сеанса.

Ниже приведены некоторые наиболее часто используемые переменные окружения:

Имя Описание
HOME Определяет путь к домашнему каталогу текущего пользователя
PWD Определяет текущий активный каталог
OLDPWD Предыдущий активный каталог
SHELL Определяет программу-командную оболочку
TERM Содержит имя текущей запущенной программы-терминала
PAGER Определяет программу для постраничного вывода страниц справочных руководств
EDITOR Определяет программу для редактирования текстовых файлов
VISUAL Определяет программу для редактирования текстовых файлов с расширенными возможностями (vim, emacs)
MAIL Определяет путь к каталогу, в котором должны храниться файлы входящей электронной почты.
BROWSER Определяет веб-браузер, запускаемый по-умолчанию
ftp_proxy

http_proxy

Определяют, соответственно адреса для прокси-серверов по FTP и HTTP протоколам
MANPATH Определяет каталог, в котором содержатся подкаталоги, содержащие man-страницы справочных руководств системной справки для команды man
INFODIR Определяет список каталогов для поиска info-страниц для команды info
TZ Определяет временную зону. Доступные временные зоны хранятся в /usr/share/zoneinfo

Системные переменные

Согласно стандартам Linux переменные окружения для организации системной среды хранятся в нескольких файлах:

  • /etc/bash.bashrc – хранение переменных для командных оболочек;
  • /etc/profile – хранение переменных для интерактивных оболочек;
  • /etc/environment – переменные из этого файла используются модулем

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

Пользовательские переменные

Для систем, ориентированных на многопользовательский доступ, например на хостинговых площадках, конкретному пользователю полезно иметь возможность гибко настраивать своё собственное рабочее окружение, создавая новые переменные или дополняя их новыми значениями. К примеру, владельцу сайта, для его виртуального хоста, обслуживающего сайт на CMS Drupal, захотелось установить утилиту drush, которая облегчает и ускоряет обслуживание CMS. В этом случае достаточно после локальной установки (в каком-нибудь подкаталоге домашнего каталога) drush, дополнить переменную PATH значением, содержащим путь к утилите drush в файле ~/.bashrc. В результате командная оболочка сможет запускать команду drush для данного пользователя.

По стандартам соглашений для Linux-систем конфигурация для пользовательского окружения должна храниться в следующих файлах:

  • ~/.bashrc – для хранения параметров инициализации выбранной командной оболочки для пользователя;
  • ~/.profile – для инициализации доступных пользователю командных оболочек;
  • ~/.pam_environment – для использования модулем

Временные или переменные сеанса

Переменные окружения также можно устанавливать временно. Это значит, что переменная будет действовать только во время действия текущего сеанса пользователя. В таких случаях удобно иметь определённый скрипт, содержащий код с необходимыми переменными, который можно в любое время запускать командой:

$ source файл_скрипта

Или же устанавливать их вручную командой export.

[править] Мультиплексирование терминала

создать на одном терминале несколько можно с помощью одной из этих программ:

  • screen (GNU Screen);
  • tmux;
  • pymux (клон tmux на Python) или другие клоны.

Поговорим о них, и рассмотрим их преимущества и недостатки.

GNU Screen — программа наиболее старая и заслуженная из этого списка. Она была написана в 1987 году (ещё в долинуксовые времена), в Берлине, и потом долго время, наверное, не меньше 20 лет, была единственным полноценным консольным мультиплексором терминала.

Решала она собственно две задачи:

  1. Создание нескольких терминалов на одном и удобное переключение между ними;
  2. Работа в отключенном (detached) режиме, когда от терминала можно отключиться, а приложение продолжает работать невидимым. После этого можно подключиться и продолжить работу с ним.

Вот из-за этих двух фич screen и используется. Фичи эти достаточно очевидные и всем широко известные.

Но что может screen ещё? Какие фокусы можно делать с ним?

Одна из интересных возможностей — это запуск приложений в detached-режиме, то есть по сути превращение консольной программы в программу-демон (хотя это, формально говоря, не так, потому что процесс имеет терминал, хоть и не видимый без подключения к нему):

$ screen -S prg1 -d -m top
$

Здесь интерактивная программа top запускается в невидимом, отключенном (detached) сеансе screen, и сама команда, вызванная в шелле, мгновенно завершается (a top продолжает работать в фоне). Конечно, это не имеет особого смысла для top, но есть много программ и (в особенности самодельных) скриптов, которые вы хотите запустить и забыть, периодически подключаясь к ним и посматривая, что они делают, и взаимодействуя с ними интерактивно.

(аналогично можно было бы поступить запустив программу с помощью nohup, но во-первых, она не предоставляет терминала, а во-вторых, к ней потом нельзя подключиться и как-то повлиять, можно только смотреть log-файл)

Если программа запускается из загрузочного скрипта, она будет работать под root’ом, если же такие мощные права не нужны, ограничить их можно с помощью su:

$ screen -S prg2 -d -m su - nobody /usr/bin/prg2

Сейчас программа будет выполняться не под root’ом, а под пользователем nobody.

Имена сеансов (prg1, prg2 и т.д.) нужны для того чтобы удобнее впоследствии подключаться к работающей программе.

$ screen -ls # просмотр работающих сеансов
$ screen -r -D -S prg1 # подключиться к сеасну prg1 (отключиться потом: ctrl-a d)

screen имеет возможности логирования происходящего в нём, но ещё удобнее это сделать, скомбинировав вызов программы с программой script, о которой мы говорили в прошлый раз:

$ screen -S prg1 -d -m script -c my-mega-script.sh screen-log

Что ещё может screen хорошего, о чём стоит знать?

  • Организовывать совместный доступ к терминалу нескольким пользователям (или из нескольких мест);
  • Открывать несколько терминалов одновременно в одном окне (ключевое слово: регионы);
  • Вводить текст в консоль от имени пользователя.

Однако, мы не будем останавливаться на этих его фичах — делает он это хорошо, но есть программы, которые делают это ещё лучше. Переходим к ним, если же вы хотите больше узнать о screen: xgu.ru/wiki/man:screen (man screen в русском переводе).

Появившись в 1987 году, ещё даже за 4 года до появления Linux, screen очень быстро стал стандартом де-факто как консольный оконный менеджер (или мультиплексор терминалов). Он полностью занимал эту нишу, и работал настолько хорошо, что альтернатив ему не требовалось. Написать альтернантиву было не сложно, как мы увидим чуть позже, потом их появилось сразу несколько (и некоторые из них настолько простые, что реализовать их смогут многие из вас буквально за пару дней кодинга), но они были не нужны.

В 2007 году, 20 годами позже, появилась реальная альтернатива screen’у, которая набирает всё большую и большую популярность: tmux.

В чём её принципиальные отличия?

  1. tmux очень простой, написан с нуля, и из-за этого легко расширяется и быстро развивается (в отличие от screen который остался практически в первозданном виде);
  2. tmux имеет BSD-лицензию, а не лицензию GNU как screen, что импонирует пользователям BSD-систем и фанатам полный свободы (в BSD-шном смысле этого слова);
  3. tmux разделяет понятия окна и панели, делая управление ими намного удобнее и гибче (screen пытался делать это с помощью регионов, но получилось довольно плохо);
  4. (и это, на мой взгляд, самое главное) tmux имеет полноценную клиент-серверную архитектуру, что позволяет независимо взаимодействовать со всеми его панелями, окнами и сеансами.

Ничего не устанавливайте в глобальный интерпретатор

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

Установка начинается, прогресс-бары заполняются, но в итоге всё завершается
чем-то типа такого:

Можно попробовать установить, используя , и это сработает, но это
считается очень плохой практикой,
и я настоятельно рекомендую так не делать по нескольким причинам:

  1. Угроза безопасности.

    В секции про установку пакетов я упомянул, что для пакетов других форматов,
    кроме wheel, могут потребоваться дополнительные действия. На самом деле,
    при установке пакета формата исполняется файл, в котором
    потенциально могут содержаться любые действия — от честной установки пакета,
    до или установки криптомайнера в систему. Т.к. в PyPI пакет
    загрузить может кто угодно, никогда нельзя быть уверенным, что именно сделает пакет
    при установке. Выполнять такой скрипт с системными привилегиями () —
    не самый мудрый ход.

  2. Может нарушить целостность системы.

    Часто в операционных системах принято устанавливать программы через
    пакетный менеджер (будь то , или ). Этот же пакетный
    менеджер затем может без следа удалить установленную программу, потому что он
    ведёт учёт файлов — какой программе какие файлы принадлежит. Если начать
    изменять файлы программ каким-либо образом, помимо пакетного менеджера,
    то это может нарушить его работу. , конечно, установит что нужно,
    но после этого могут возникнуть проблемы с системным пакетным менеджером.

Как правильно:

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

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

    К получившемуся пути нужно в конце добавить для Linux и MacOS,
    либо для Windows, так что
    в моём случае (MacOS) в нужно добавить вот такой путь:
    .

    Подробнее про этот метод установки читайте .

  2. установить программу через пакетный менеджер ОС, например:

    Часто мейнтейнеры ОС создают обёртки для пакетов из PyPI, которые можно
    установить при помощи системного пакетного менеджера. Как правило, такие
    обёртки называются или .
    Это делается как раз для того, чтобы дать пользователям возможность
    устанавливать Python-программы, не нарушая работу пакетного менеджера ОС.
    Кроме того, эти пакеты проходят проверку безопасности, так что риск получить
    криптомайнер значительно ниже.

Выводы

  • всегда устанавливайте зависимости проектов в отдельные виртуальные окружения;
  • если нужно установить пакет «глобально», то используйте либо ,
    либо прибегните к помощи пакетного менеджера операционной системы;
  • никогда не используйте — считайте, что это табу.

Да, виртуальные окружения — определенно не самая удобная часть разработки на
Python, и уж точно не самая простая тема, к этому просто нужно привыкнуть.
Несколько раз повторил, выработал привычку — в целом, ничего сложного.
Кроме того, экосистема Python развивается очень быстро, и я надеюсь,
что скоро правильная установка пакетов
и управление виртуальными окружениями станут намного легче.
Уже сейчас можно пользоваться такими инструментами,
которые в некоторой мере прячут от пользователя виртуальные окружения:

  • poetry;
  • pipenv.

Стабильных вам зависимостей и кода без багов!

Детали реализации

Unix-подобные системы

В прошлом Unix и Unix-подобные системы использовали устройства с последовательным портом, такие как порты RS-232 , и предоставляли для них файлы устройств .

С помощью эмуляторов терминала эти файлы устройств эмулируются с помощью пары псевдотерминальных устройств. Эта пара используется для имитации физических портов / подключения к конечной вычислительной точке хоста — компьютерному оборудованию, предоставляемому API операционной системы, некоторым другим программным обеспечением, таким как rlogin , telnet или SSH, или другим. Например, в системах Linux это будут (для ведущей стороны) и (для ведомой стороны) псевдотерминальные устройства соответственно.

Существуют также специальные файлы виртуальной консоли, например . В текстовом режиме запись в файл отображает текст на виртуальной консоли, а чтение из файла возвращает текст, который пользователь записывает в виртуальную консоль. Как и в случае с другими текстовыми терминалами , есть также специальные escape-последовательности , управляющие символы и функции, которые программа может использовать, проще всего через библиотеку, такую ​​как ncurses . Для более сложных операций программы могут использовать специальные системные вызовы ioctl консоли и терминала . Можно сравнивать устройства, используя шаблоны vcs («экран виртуальной консоли») и vcsa («экран виртуальной консоли с атрибутами»), такие как и .

Виртуальные консоли можно настроить в файле, читаемом init — обычно он запускает процесс входа в текстовый режим getty для нескольких виртуальных консолей. Систему X Window можно настроить в диспетчере отображения X или с помощью него . В ряде дистрибутивов Linux вместо init используется systemd , что также позволяет настраивать виртуальную консоль.

Инструменты командной строки

Типичные системные программы Linux, используемые для доступа к виртуальным консолям, включают:

  • переключить текущую виртуальную консоль
  • запустить программу на новой виртуальной консоли
  • закрыть неиспользуемую в настоящее время виртуальную консоль

Загрузка системы

Программа startx запускает систему X Window на новой виртуальной консоли. Есть также другие графические программы , которые можно начать с консоли (например , LinuxTV и MPlayer и т.д.)

Синхронные терминалы

В асинхронных терминалах данные могут передаваться в любом направлении в любое время. В синхронных терминалах протокол контролирует, кто и когда может отправлять данные. Терминалы на базе IBM 3270, используемые с мэйнфреймами IBM, являются примером синхронных терминалов . Они работают в основном в режиме «экран за раз» (также известный как блочный режим ). Пользователи могут вносить многочисленные изменения на страницу перед отправкой обновленного экрана на удаленный компьютер как одно действие.

Эмуляторы терминала, имитирующие протокол 3270, доступны для большинства операционных систем, для использования как теми, кто управляет системами, такими как z9 , так и теми, которые используют соответствующие приложения, такие как CICS .

Другие примеры синхронных терминалов включают IBM 5250 , ICL 7561, Honeywell Bull VIP7800 и Hewlett-Packard 700/92.

Переменные окружения и переменные оболочки

Переменные можно разделить на две основные категории:

   Переменные окружения (или «переменные среды») — это переменные, доступные в масштабах всей системы и наследуемые всеми дочерними процессами и оболочками.

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

Все переменные имеют следующий формат:

При этом также следует придерживаться определенных правил:

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

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

   Вокруг символа не должно быть пробелов.

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

   команда env — позволяет запускать другую программу в пользовательском окружении без изменения в текущем окружении. При использовании без аргумента выведет список переменных текущего окружения;

   команда printenv — выводит список всех переменных окружения (или какую-то отдельно заданную переменную);

   команда set — устанавливает переменные оболочки. При использовании без аргумента выведет список всех переменных, включая переменные окружения и переменные оболочки, а также функции оболочки;

   команда unset — удаляет переменные оболочки и переменные окружения;

   команда export — создает переменную окружения.

51. Этап компиляции загрузки и выполнения

  • Этап компиляции (Compile time). Когда на стадии компиляцииизвестно точное место размещения процесса в памяти, тогда непосредственногенерируются физические адреса. При изменении стартового адреса программынеобходимо перекомпилировать ее код.
  • Этап загрузки (Load time). Если информация о размещениипрограммы на стадии компиляции отсутствует, компилятор генерирует перемещаемыйкод. В этом случае окончательное связывание откладывается до моментазагрузки. Если стартовый адрес меняется, нужно всего лишь перезагрузить код сучетом измененной величины.
  • Этап выполнения (Execution time). Если процесс может бытьперемещен во время выполнения из одной области памяти в другую,с вязывание откладывается до стадиивыполнения. Здесь желательно наличие специализированного оборудования, напримеррегистров перемещения. Их значение прибавляется к каждому адресу,сгенерированному процессом. Большинство современных ОС осуществляет трансляциюадресов на этапе выполнения,используя для этого специальный аппаратный механизм.

Создание списка установленных пакетов Requirements.txt

Используя виртуальные окружения можно легко создавать файл, в котором будут собраны все названия и версии пакетов для определенного проекта. Этот подход используется для удобства работы, так как одной программой мы сразу установим нужные пакеты.

Так мы получим список пакетов, установленных в виртуальном окружении, в формате читаемом pip:

Следующим способом мы экспортируем этот список в файл Requirements.txt (способ подходит для Bash/CMD/Powershell):

На другом компьютере/среде мы можем этот список быстро проверить и установить отсутствующие пакеты следующей командой:

 

Рекомендую

3. В чем разница между режимом ядра и пользовательским режимом? Объясните, как сочетание двух отдельных режимов помогает в проектировании операционных систем.

Большинство компьютеров имеют два режима работы: режим ядра и режим пользователя. Операционная система — наиболее фундаментальная часть программного обеспечения, работающая в режиме ядра (этот режим называют еще режимом супервизора). В этом режиме она имеет полный доступ ко всему аппаратному обеспечению и может задействовать любую инструкцию, которую машина в состоянии выполнить. Вся остальная часть программного обеспечения работает в режиме пользователя, в котором доступно лишь подмножество инструкций машины. В частности, программам, работающим в режиме пользователя, запрещено использование инструкций, управляющих машиной или осуществляющих операции ввода-вывода (Input/Output — I/O).

98. API (схема взаимодействия). API сокетов

API — набор готовых классов, процедур, функций, структур и констант, предоставляемых приложением или операционной системой для использования во внешних программных продуктах. Используется программистами при написании всевозможных приложений.

Для использования сокетов в UNIX разработчикам предоставляется API сокетов Беркли:

socket() — создаёт конечную точку соединения и возвращает дескриптор

gethostbyname(), gethostbyaddr() — возвращают описание интернет-узла

connect() — устанавливает соединение с сервером

bind() — связывает сокет с адресом

listen() — подготавливает сокет к принятию входящих соединений

accept() — принимает запрос на установление соединения

4.5 Љоманды терминала

’ерминал управлЯетсЯ компьютером. Љомпьютер посылает терминалу
текст не только длЯ отображениЯ на экране, но также и посылает команды
терминалу, которые должны выполнЯтьсЯ. Ћни называютсЯ «управлЯющими кодамми»
(байтами) и «escape-последовательностЯми». Ќапример, управлЯющий код CR
(возврат каретки) перемещает курсор на левый край экрана. ЌекотораЯ
escape-последовательность (несколько байтов, где первый байт — управлЯющий код
«Escape») может перемещать курсор в место на экране, определенное параметрами,
помещенными внутри escape-последовательности.

«Џервые терминалы» имели только несколько таких команд, но современные
терминалы, имеют их сотни. ‚ид дисплеЯ может быть изменен длЯ некоторых
параметров: типа Яркости, тусклости, подчеркиваниЯ, миганиЯ и негативного
изображениЯ. „инамик в терминале может «пищать», когда нажата какаЯ-то клавиша
или подавать звуковой сигнал, если произошла ошибка. ”ункциональные клавиши
могут программироватьсЯ длЯ специальных значений. Њожно подключать различные
шрифты. „исплей может быть пролистан вверх или вниз. Ћпределенные части экрана
могут быть стерты. Њогут использоватьсЯ различные типы управлениЯ потоком
данных, чтобы остановить поток данных, когда байты посланы терминалу быстрее,
чем терминал упевает их обрабатывать. €меетсЯ еще много чего, о чем вы можете
прочесть в руководстве по терминалу.

[править] Запись терминальной сессии

Программа script предназначена для создания лога терминальной сессии. Она выделяет дочерней программе терминал, происходящее на котором попадает в файл.

$ script
Script started, file is typescript
$ # действия которые записываются
$ exit
Script done, file is typescript

В данном случае имя файла с логом работы — typescript.

Позже его можно просматривать, анализировать, делиться им с другими.

Есть несколько интересных проектов, которые развивают идею script:

ttyrecord, где записывается не только поток символов, но и временные метки, что позволяет в будущем не просто просмотреть журнал, а прокрутить кино, как это было;

asciinema, развивает эту идею и представляет кино прямо в веб — запись терминала можно просматривать не только в терминале, а прямо в браузере, и что ещё более важно — делиться ею; этакой youtube для действий в терминале;

[править] Перехват вывода терминала

Эти все программы хорошо известны и многими используются. Тут ничего нового. А вот более интересный вопрос:

А можно ли вести запись уже открытого терминала, если программа уже работает, или если нет возможности запустить script перед ней?

Да, такая возможность есть, даже несколько, хотя и не очевидны.

В BSD-системах это можно сделать с помощью watch (там watch делает совсем другое чем в Linux), правда это работает только с виртуальными терминалами, но не работает с pty.

В Linux это можно сделать через SystemTap, прямо руками:

#!/usr/bin/stap
probe kernel.function("pty_write") {
  if (kernel_string($tty->name) == @1) {
    printf("%s", kernel_string_n($buf, $c))
  }
}

SystemTap вещь очень мощная, но это уже слишком, из пушки по воробьям. Кроме того, что нужно устанавливать собственно SystemTap, нужны ещё отладочные символы ядра (kernel*dbg) и его заголовочные файлы. И, конечно, нужны права рута.

$ ispy PID

показывает то, что выводится на терминал, к котором подключен процесс PID. Это вывод не только самого процесса, но и его потомков.

Не только посмотреть, но и записать, что там происходит:

$ script -c 'ispy PID'

ispy использует ptrace (трассировку системных вызовов) и пытается на основе перехваченных вызовов воссоздать картину происходящего на терминале. Получается более-менее, но не идеально (попробуйте понаблюдать за процессами использующими терминал от всей души).

Все описанные решения (systemtap, watch, ispy и другие неназванные) являются хаками, влезающими в терминал каким-то непредусмотренным способом. Их следует всегда по возможности избегать, если есть возможность легальной записи происходящего на терминале, с помощью таких программ как script.

Рейтинг
( Пока оценок нет )
Понравилась статья? Поделиться с друзьями:
Все про сервера
Добавить комментарий

;-) :| :x :twisted: :smile: :shock: :sad: :roll: :razz: :oops: :o :mrgreen: :lol: :idea: :grin: :evil: :cry: :cool: :arrow: :???: :?: :!: