Терминал и Git за один вечер
Что такое терминал и зачем он нужен, два десятка команд для работы с папками и файлами, а потом Git — «машина времени» для кода: как сохранять версии своей работы и хранить решения в репозитории курса.
Зачем это нужно
Заголовок раздела «Зачем это нужно»Почти всё, что вы будете делать в курсе, делается командами. Установить библиотеки — pip install.
Скачать модель — git clone. Запустить обучение — python train.py. Посмотреть, чем занята видеокарта, — nvidia-smi.
У арендованного сервера с GPU вообще нет рабочего стола: только чёрное окно и мигающий курсор.
терминалтерминал (командная строка)EN terminal / command lineОкно, где команды вводятся текстом. Исполняет их оболочка (shell) — bash или zsh. — это и есть такое окно. Вы набираете команду текстом, нажимаете Enter, и компьютер
печатает ответ. Внутри окна работает программа-оболочка (shell): она читает ваши команды и запускает нужные
программы. В Linux и WSL оболочка обычно называется bash, в macOS — zsh; для команд этого урока разницы нет.
Терминал кажется неудобным ровно до тех пор, пока вы не выучите два десятка команд. После этого он быстрее любой мыши: команду можно повторить, записать в файл-скрипт и отправить коллеге, а щелчок мышью — нельзя.
Вторая половина урока — система контроля версийсистема контроля версийEN version control system (VCS)Хранит историю изменений файлов. Самая популярная — Git. Git. Он хранит историю вашего кода: можно откатиться к вчерашней версии, попробовать безумную идею в отдельной ветке и перенести работу на другой компьютер. Открытые LLM — DeepSeek, Qwen, MiniMind — публикуют свой код в Git-репозиториях. Курс ChinaAI тоже живёт в Git, и свои решения вы будете сохранять туда же.
Где взять терминал
Заголовок раздела «Где взять терминал»Рекомендуем WSL (Windows Subsystem for Linux) — настоящий Linux внутри Windows. Откройте PowerShell от имени администратора и выполните:
wsl --installПосле перезагрузки появится приложение Ubuntu — это и есть ваш терминал. Работайте в домашней папке Linux
(~), а не в /mnt/c/...: там файлы открываются в разы быстрее. В VS Code поставьте расширение WSL,
и редактор будет работать прямо внутри Linux. PyTorch с CUDA в WSL 2 тоже работает.
Сайт курса и домашние работы можно запускать и из обычного PowerShell — так описано на странице «Установка и запуск». WSL нужен, чтобы команды этого урока работали как в Linux, и пригодится с 4-й ступени: vLLM и ряд других GPU-инструментов курса работают только в Linux.
Запасной вариант — Git Bash (ставится вместе с Git for Windows). Для Git его хватает, но многие Linux-инструменты там урезаны.
Откройте Terminal (Spotlight → «Terminal»). С macOS Catalina оболочка по умолчанию — zsh, все команды урока
в ней работают. Git ставится вместе с инструментами разработчика:
xcode-select --installТерминал уже есть (обычно Ctrl+Alt+T). Git ставится пакетным менеджером дистрибутива,
например sudo apt install git в Ubuntu. Слово sudo в начале значит «выполнить от имени администратора»:
система спросит ваш пароль. Оно нужно только для установки программ в систему, а не для повседневной работы.
Анатомия команды
Заголовок раздела «Анатомия команды»grep -i --count "error" train.log| Часть | Пример | Что это |
|---|---|---|
| команда | grep | программа, которую запускаем: поиск строк в файле |
| короткий флаг | -i | меняет поведение: не различать регистр букв (заглавные и строчные: Error = error) |
| длинный флаг | --count | то же, но словом: вывести не строки, а их количество |
| аргументы | "error", train.log | что искать и где |
Целиком команда читается так: «найди в файле train.log строки со словом error, не глядя на регистр, и скажи,
сколько их». Части разделяются пробелами — поэтому пробелы в именах файлов доставляют неудобства (о них ниже).
Справку почти любая команда печатает по --help (например, grep --help), подробное руководство открывает man grep.
Выход из man — клавиша q.
Где я и что вокруг
Заголовок раздела «Где я и что вокруг»Файловая система — это дерево папок. Терминал всегда «стоит» в какой-то из них: это текущая папка.
pwd # где я? → /home/ivanls # что здесь?ls -la # подробно, включая скрытые файлы (имя начинается с точки)cd projects # зайти в папку projectscd .. # подняться на уровень вверхcd ~ # домой (или просто cd)cd - # вернуться в предыдущую папку| Запись | Значение |
|---|---|
/ | корень всей файловой системы |
~ | ваша домашняя папка |
. | текущая папка |
.. | родительская папка |
/home/ivan/notes | абсолютный путь — от корня, работает откуда угодно |
notes/carp.txt | относительный путь — от текущей папки |
Создать, посмотреть, переместить, удалить
Заголовок раздела «Создать, посмотреть, переместить, удалить»mkdir -p ml/notes # создать папку (-p: вместе с родительскими, без ошибки, если уже есть)cd ml/notestouch todo.txt # создать пустой файлcp todo.txt todo_backup.txt # копироватьmv todo_backup.txt old.txt # переместить или переименоватьrm old.txt # удалить файлcat todo.txt # вывести файл целикомless big_log.txt # листать большой файл (q — выход, / — поиск)head -n 5 data.csv # первые 5 строкtail -n 20 train.log # последние 20 строкtail -f train.log # следить за файлом в реальном времени — так смотрят на лог обученияПеренаправление и конвейеры
Заголовок раздела «Перенаправление и конвейеры»У каждой команды есть стандартный вывод (то, что она печатает) и стандартный ввод. Их можно перенаправлять:
echo "первая заметка" > log.txt # echo печатает текст; > — записать его в файл (старое содержимое пропадёт!)echo "вторая заметка" >> log.txt # >> — дописать в конецcat log.txtпервая заметкавторая заметкаконвейерконвейер (пайп)EN pipeСимвол | передаёт вывод одной команды на вход следующей. | соединяет команды: вывод левой становится вводом правой. Это главная идея Unix —
маленькие программы, каждая делает одно дело, а сложное получается их сцеплением.
Создадим файл с текстом и посчитаем самые частые слова, не написав ни строчки на Python:
cat > carp.txt << 'EOF'карп плывёт против течениякарп прыгает через вратакто перепрыгнет врата тот станет драконом#прыжоквдраконыEOF
cat carp.txt | tr ' ' '\n' | sort | uniq -c | sort -rn | head -3 2 карп 2 врата 1 черезКонструкция cat > carp.txt << 'EOF' … EOF записывает в файл все строки между двумя EOF. Это удобно, когда
текст нужно вставить прямо из урока. Тот же файл можно создать и в обычном редакторе, результат одинаковый.
Теперь разберём конвейер по звеньям: cat выводит файл, tr ' ' '\n' заменяет пробелы переводами строк
('\n' — так записывают невидимый символ «новая строка»; получается одно слово — одна строка),
sort сортирует, uniq -c схлопывает одинаковые соседние строки и пишет их количество, sort -rn сортирует
по числу по убыванию (-r — в обратном порядке, -n — сравнивать как числа), head -3 оставляет первые три.
Каждое звено получает на вход то, что напечатало предыдущее. Все слова, кроме «карп» и «врата», встречаются по одному
разу, и порядок между ними зависит от настроек системы. Поэтому третья строка у вас может оказаться другой.
Обратите внимание на последнюю строку файла. Хэштег «#прыжоквдраконы» — это фраза из трёх слов: «прыжок», «в», «драконы».
Но пробелов между ними нет, и tr считает всю фразу одним словом (уберите из конвейера | head -3 — и увидите её в списке целиком).
Так же, без пробелов между словами, пишут по-китайски: там вся фраза — сплошная цепочка иероглифов.
Разбить такой текст на части — отдельная задача, и называется она токенизация. Ей посвящён модуль 4.2.
Ещё один полезный инструмент — grep, поиск строк по образцу:
grep "врата" carp.txt # строки, где есть «врата»grep -c "карп" carp.txt # сколько таких строк → 2grep -i "error" train.log # без учёта регистра — найти ошибки в логеwc -l carp.txt # число строк → 4Байты и символы
Заголовок раздела «Байты и символы»Сравните:
printf 'carp' | wc -c # 4 байтаprintf 'карп' | wc -c # 8 байтprintf '€' | wc -c # 3 байтаprintf '🐟🐉' | wc -c # 8 байтprintf '🐟🐉' | wc -m # 2 символаwc -c считает байты, wc -m — символы. Здесь printf, а не echo, потому что echo добавляет в конец
невидимый перевод строки, а это ещё один байт. Помните из урока
«Как компьютер выполняет программу»: в кодировке UTF-8 латинская буква
занимает 1 байт, кириллическая — 2, знак € (как и китайский иероглиф) — 3, эмодзи — 4. Терминал показывает то же самое. Из-за этого один и тот же смысл «стоит» разное число байтов на разных языках.
Многие современные токенизаторы — программы, которые режут текст на токены для LLM (byte-level BPE в GPT-2, Qwen и других), — работают именно с байтами, и к этому факту мы вернёмся на ступени 4.
Частые ошибки в терминале
Заголовок раздела «Частые ошибки в терминале»| Сообщение | Что значит | Что делать |
|---|---|---|
command not found | такой программы нет или опечатка в имени | проверьте написание; если программа не установлена — поставьте её (sudo apt install …) |
No such file or directory | файла или папки по этому пути нет | pwd и ls — где вы и что рядом. Проверьте регистр букв: Data и data — разные имена |
Permission denied | не хватает прав | не добавляйте sudo бездумно. Для своих файлов в домашней папке права администратора не нужны |
Is a directory | команде нужен файл, а вы дали папку | например, cat notes/ → нужно ls notes/ или cat notes/carp.txt |
| курсор мигает, ничего не происходит | команда ждёт ввода (например, cat без файла) или долго работает | Ctrl+C — прервать |
Пробелы в именах файлов — частый источник проблем: cd my notes терминал поймёт как две папки. Либо берите имя
в кавычки (cd "my notes"), либо, что лучше, называйте файлы без пробелов.
Привычки, которые экономят часы
Заголовок раздела «Привычки, которые экономят часы»- Tab дописывает имя файла или команды. Нажмите дважды — увидите варианты. Не набирайте длинные пути руками.
- ↑ / ↓ — листать историю команд. Ctrl+R — поиск по истории: начните набирать
pipи найдёте вчерашнюю длинную команду. - Ctrl+C прерывает зависшую или слишком долгую команду, например обучение, которое пошло не туда.
- Ctrl+L или
clearочищает экран. history | grep git— вспомнить, что вы делали с Git.
Переменные окружения и PATH
Заголовок раздела «Переменные окружения и PATH»Терминал хранит переменные окружения — именованные настройки, которые видят все запущенные из него программы.
Знак $ перед именем значит «подставь сюда значение переменной»:
echo $HOME # ваша домашняя папкаecho $PATH # список папок, где ищутся командыwhich python3 # какой именно python3 запуститсяexport HF_HOME=~/hf-cache # задать переменную в текущем сеансеHF_HOME из последней строки — папка, куда библиотеки Hugging Face складывают скачанные модели (понадобится
на ступенях 6–8). Переменная, заданная через export, живёт, пока открыто это окно терминала.
Когда вы набираете python3, оболочка по очереди просматривает папки из PATH и запускает первый найденный
файл с таким именем. Именно так работает «активация» виртуального окружения в следующем уроке: она ставит папку
окружения в начало PATH.
Git: машина времени для кода
Заголовок раздела «Git: машина времени для кода»Без Git история проекта выглядит так: model.py, model_v2.py, model_v2_final.py, model_v2_final_REALLY.py.
Git заменяет этот хаос одной папкой и журналом коммитов.
коммиткоммитEN commitСнимок состояния файлов с сообщением, автором и датой. — это снимок всех отслеживаемых файлов (тех, которые вы хоть раз добавили в Git командой
git add) с сообщением, автором и датой. Представьте сохранение в компьютерной игре: к нему всегда можно вернуться. Любой коммит можно
посмотреть, сравнить с другим или восстановить. Папка проекта вместе со всей историей называется
репозиториемрепозиторийEN repository (repo)Папка проекта вместе со всей историей изменений (скрытая подпапка .git).. История хранится в скрытой подпапке .git.
Изменение проходит три зоны:
flowchart LR W["Рабочая папка<br/>(вы правите файлы)"] -- "git add" --> S["Индекс (staging)<br/>что войдёт в коммит"] S -- "git commit" --> R["История коммитов<br/>(.git)"] R -- "git push" --> G["Удалённый репозиторий<br/>(GitHub)"] G -- "git pull / git clone" --> W
Зачем промежуточный индекс? Вы поправили пять файлов, а в коммит хотите положить только два, относящихся
к одной задаче. git add выбирает, git commit фиксирует.
Первый репозиторий за десять минут
Заголовок раздела «Первый репозиторий за десять минут»Один раз на компьютере представьтесь Git (имя и почта попадут в каждый коммит):
git config --global user.name "Иван Петров"git config --global user.email "ivan@example.com"git config --global init.defaultBranch mainДальше — настоящая сессия. У каждого коммита есть хеш — уникальный номер-отпечаток вроде f0ef469.
У вас хеши будут другими: они зависят от времени и автора.
mkdir my-ml-notes && cd my-ml-notes # && — выполнить вторую команду, только если первая прошла без ошибкиgit initecho "# Мои заметки по ChinaAI" > README.mdgit statusOn branch main
No commits yet
Untracked files: (use "git add <file>..." to include in what will be committed) README.md
nothing added to commit but untracked files present (use "git add" to track)git status — самая важная команда: она говорит, что происходит и что делать дальше. Вызывайте её постоянно.
git add README.mdgit commit -m "Первая заметка"[main (root-commit) f0ef469] Первая заметка 1 file changed, 1 insertion(+) create mode 100644 README.mdИзменим файл и посмотрим разницу до коммита:
echo "- 0.1: терминал и git" >> README.mdgit diffdiff --git a/README.md b/README.mdindex 9d290fe..c9c0f7c 100644--- a/README.md+++ b/README.md@@ -1 +1,2 @@ # Мои заметки по ChinaAI+- 0.1: терминал и gitПлюс — добавленная строка, минус — удалённая. git diff показывает ещё не добавленное в индекс,
git diff --staged — то, что уже добавлено и войдёт в коммит.
git add README.mdgit commit -m "Добавил урок 0.1"git log --onelinea2b1457 (HEAD -> main) Добавил урок 0.1f0ef469 Первая заметкаПометка (HEAD -> main) означает «вы здесь»: вы стоите на ветке main, и она указывает на этот коммит.
Испортили файл и хотите вернуть последнюю закоммиченную версию? git restore README.md (если вы ещё не сделали
git add; иначе сначала git restore --staged README.md).
Посмотреть файл, каким он был в старом коммите: git show f0ef469:README.md.
Ошибки Git, которые встречаются в первую неделю:
| Сообщение | Что значит | Что делать |
|---|---|---|
fatal: not a git repository | вы не внутри папки с репозиторием | cd в папку проекта или git init |
Author identity unknown | Git не знает ваше имя и почту | выполните две команды git config --global ... выше |
nothing to commit, working tree clean | изменений нет — всё уже закоммичено | это не ошибка |
nothing added to commit but untracked files present | забыли git add | git add имя_файла, затем git commit |
| в терминале открылся текстовый редактор | вы написали git commit без -m, и Git просит ввести сообщение | выйти без коммита: в Vim — :q! и Enter, в nano — Ctrl+X. Затем повторите с -m "сообщение" |
веткаветкаEN branchНезависимая линия коммитов. Позволяет экспериментировать, не ломая основную версию. — это независимая линия коммитов. Основная ветка обычно называется main.
Для эксперимента создают новую ветку: если идея не сработает, main останется нетронутой.
git switch -c experiment # создать ветку и перейти на неёgit branch # список веток; звёздочка — та, на которой вы стоитеecho "черновик" > draft.txtgit add draft.txt && git commit -m "Черновик эксперимента"git switch main # вернуться: draft.txt исчез из папки!git merge experiment # влить эксперимент в maingit log --oneline --graph --all* 47576e9 (HEAD -> main, experiment) Черновик эксперимента* a2b1457 Добавил урок 0.1* f0ef469 Первая заметкаОбе ветки, main и experiment, теперь указывают на один и тот же коммит. После git switch main файл draft.txt действительно пропадает из папки: Git показывает состояние той ветки,
на которой вы стоите. После merge файл возвращается. Здесь слияние вышло простым (fast-forward):
в main за это время ничего не менялось. Если обе ветки правили одну строку, Git попросит разрешить
конфликт вручную. Это не страшно: откройте файл, выберите нужный вариант, затем git add и git commit.
Как с ветками и pull request работают в команде и как по истории Git найти коммит, который всё сломал
(git log -S, git blame, git bisect), — в уроке 11.1.2.
Что не класть в репозиторий: .gitignore
Заголовок раздела «Что не класть в репозиторий: .gitignore»Файл .gitignore в корне проекта перечисляет то, что Git должен игнорировать:
.venv/__pycache__/*.pyc.ipynb_checkpoints/data/*.pt*.safetensors.env| Не коммитим | Почему |
|---|---|
.venv/ | окружение пересоздаётся одной командой, а весит сотни мегабайт |
*.pt, *.safetensors, data/ | веса и датасеты бывают гигабайтными. Git для них не предназначен |
.env | там API-ключи. Публичные репозитории постоянно сканируют боты, и утёкший ключ находят очень быстро |
Удалённые репозитории и курс ChinaAI
Заголовок раздела «Удалённые репозитории и курс ChinaAI»Удалённый репозиторий (remote) — копия репозитория на сервере в интернете, обычно на GitHub. GitHub — это сайт, где хранят Git-репозитории и работают над ними вместе: что-то вроде облачного диска для кода, который понимает историю коммитов. Там же лежат открытые модели и курс ChinaAI. Три главные команды:
git clone https://github.com/samnolak/ChinaAI.git # скачать репозиторий со всей историейgit pull # забрать новые коммиты с сервераgit push # отправить свои коммиты на серверРепозиторий курса закрытый (private), поэтому GitHub сначала спросит, кто вы. Обычный пароль от аккаунта здесь
не подойдёт. Проще всего один раз войти через программу GitHub CLI: команда gh auth login задаст пару вопросов
и откроет браузер для входа. Другие варианты — персональный токен (длинный пароль, который выдаёт GitHub) или SSH-ключ.
Пошагово, с установкой GitHub CLI, это описано на странице «Установка и запуск».
Если сайт курса у вас уже работает, значит, курс уже склонирован — второй раз клонировать не нужно.
После клонирования вы увидите:
ChinaAI/├── site/ # этот сайт: уроки, интерактивные задания (npm run dev — запустить локально)├── homework/ # домашние работы: условия, заготовки code/, тесты tests/, автогрейдер check.py├── solutions/ # эталонные решения ДЗ — заглядывайте только после своей попытки└── docs/ # документация проектаКак хранить свой прогресс
Заголовок раздела «Как хранить свой прогресс»Предлагаем простой рабочий процесс с отдельной веткой study:
- Один раз создайте свою ветку:
git switch -c study. Все решения пишите здесь. - Решили задание — закоммитьте:
git add homework/s0-hw01-python-warmup && git commit -m "s0-hw01: Medium". Автогрейдер сохраняет ваш лучший уровень вhomework/.results.json. Этот файл стоит в.gitignoreкурса, и Git его не коммитит: уровни всегда можно восстановить, снова запустивcheck.pyна своих решениях. - Прогресс сайта (пройденные уроки, квизы, карточки) хранится в браузере. Периодически экспортируйте его
в JSON на странице «Карта курса и прогресс», положите файл в папку
journal/progress/и закоммитьте. Пошагово это описано на странице «Установка и запуск». - Курс обновился? Заберите обновления и влейте их в свою ветку:
git switch main && git pull && git switch study && git merge main. - Хотите резервную копию на GitHub —
git push -u origin study.
Чтобы предложить правку в чужой открытый проект, понадобятся ещё форк, второй удалённый репозиторий upstream
и pull request — это разобрано в уроке 11.4.2.
Практика
Заголовок раздела «Практика»Пройдите этот сценарий целиком. Он занимает 15–20 минут и закрепляет всё, что было в уроке.
- Создайте папку
~/chinaai-sandbox, зайдите в неё, сделайтеgit init. - Создайте файл
carp.txtс текстом из урока (черезcat > carp.txt << 'EOF') и посчитайте частоты слов конвейером. Сохраните результат:... | head -3 > top.txt. - Закоммитьте оба файла с сообщением «Частоты слов». Проверьте
git log --oneline— там должен быть один коммит. - Создайте ветку
experiment, допишите вcarp.txtстроку «карп стал драконом» (через>>) и закоммитьте с сообщением «Карп стал драконом». Подсказка:git commit -am "..."сразу добавляет в индекс все изменённые отслеживаемые файлы. - Вернитесь в
mainи убедитесь командойtail -1 carp.txt, что новой строки там нет. Влейте ветку:git merge experiment. - Добавьте
.gitignoreсо строкой*.pt, создайте пустой файлmodel.ptи убедитесь, чтоgit statusего не видит. - Найдите папку курса. Если вы уже клонировали его, когда запускали сайт, перейдите в эту папку (
cdи путь к ней) и выполнитеls homeworkиgit log --oneline -5— последние 5 коммитов курса. Если ещё не клонировали — перейдите в домашнюю папку (cd ~), чтобы не создать репозиторий внутри песочницы, склонируйте курс и выполнитеls ChinaAI/homework.
Ожидаемый результат шагов 5–6 (хеши у вас будут другими):
$ git log --oneline --graph --all* 1a5e66f (HEAD -> main, experiment) Карп стал драконом* b3fb89f Частоты слов$ git status --short?? .gitignoremodel.pt в списке нет: Git его игнорирует. Сам .gitignore виден как новый файл, его тоже нужно закоммитить.
Проверь себя
Заголовок раздела «Проверь себя»- Терминал — основной интерфейс ML-инженера:
pwd,ls,cd,mkdir,cat,head,tail,grep. >перезаписывает,>>дописывает,|склеивает команды в конвейер.- В UTF-8 латиница занимает 1 байт, кириллица — 2, знак € — 3, эмодзи — 4. Это пригодится на ступени 4, когда дойдём до токенизации.
- Git:
add→commit→push. Постоянно смотритеgit status, эксперименты ведите в ветках, а секреты и веса держите в.gitignore. - Свои решения храните в ветке
study, обновления курса забирайте черезgit pullиgit merge main.
Дальше: ставим Python, создаём виртуальное окружение и запускаем Jupyter. Заодно разберёмся, где взять GPU.