Что такое…

source или ./script.sh — почему скрипт отработал, а ничего не изменилось

Иллюстрация: робот в маленькой комнате-копии переставляет мебель, а настоящая комната за дверью остаётся прежней

Ты написал скрипт из двух строк: перейти в папку и задать переменную. Запустил. Ошибок нет. А ты всё там же, и переменная пустая:

$ cat t.sh
cd /tmp
export FOO=bar
$ ./t.sh
$ pwd
/home/me/project
$ echo "FOO=$FOO"
FOO=

Скрипт отработал честно. Просто всё случилось не в твоём терминале. А вот так случится в твоём:

$ source ./t.sh
$ pwd
/tmp
$ echo "FOO=$FOO"
FOO=bar

В чём разница между source и запуском

Запуск создаёт новый процесс: отдельный bash со своей папкой и своими переменными. Закончился скрипт — закончился и этот bash вместе со всем, что он менял.

source читает файл и выполняет его строки прямо в твоём текущем терминале, как будто ты набрал их руками.

Сравним по пунктам:

  • Где идёт работа. ./t.sh — в дочернем процессе. source t.sh — в текущем.
  • Что остаётся после. Запуск не оставляет ничего: ни cd, ни переменных, ни функций. source оставляет всё.
  • Какие переменные видит. Запущенный скрипт видит только переменные окружения, то есть то, что ты сделал через export. source видит и обычные: A=1; ./k.sh напечатает пусто, а source k.sh — единицу.
  • Нужны ли права. ./t.sh требует права на запуск. source только читает файл, поэтому ему хватит и обычных прав на чтение.
  • Что делает exit. В запущенном скрипте — завершает скрипт. В source — закрывает твой терминал. Вместо него пиши return.
  • Как работает set -e. В запущенном скрипте — останавливает скрипт. Через source эта настройка остаётся в твоём терминале, и он закроется при первой же ошибке.

Почему запуск не может изменить твой терминал

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

Скрипт может как угодно менять свою копию. В твой терминал ничего не вернётся. Обратного канала для переменных просто нет. И папка у каждого процесса своя.

Представь, что тебе выдали фотокопию документа. Можешь исписать её вдоль и поперёк, а оригинал останется чистым.

Где ты с этим встречаешься

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

  • source .venv/bin/activate. Весь смысл активации в том, чтобы поменять PATH твоего терминала. Запуском этого не сделать. Поэтому файл activate даже не помечен как исполняемый.
  • nvm use 20. nvm — это не программа, а функция внутри твоего shell. По той же причине: программа не смогла бы поменять твой PATH.
  • source ~/.zshrc. Применяет новые настройки здесь и сейчас, без открытия нового окна.

Один частый промах с .env. Файл со строками API=1 через source создаст обычную переменную, а дочерние программы её не увидят:

$ source .env
$ bash -c 'echo [$API]'
[]
$ set -a; source .env; set +a
$ bash -c 'echo [$API]'
[1]

Команда set -a автоматически экспортирует всё, что объявлено после неё.

И предупреждение: source выполняет файл как код. Строка $(rm -rf ~) внутри .env сработает. Чужие файлы так не подключай.

Почему это ломает ИИ-агентов

Вот ситуация из свежей практики. Агент делает source .venv/bin/activate одним шагом, а pytest — следующим. И получает command not found или системный Python.

Причина та же самая. Во многих агентах каждая команда запускается в новом shell, и активация умирает вместе с ним. Лечится просто, и это можно прямо написать агенту в правилах:

  • Склей в одну команду: source .venv/bin/activate && pytest. Здесь важно именно &&, а не точка с запятой: если активация упала, тесты не запустятся.
  • Или не активируй вообще: .venv/bin/pytest. Этот путь работает откуда угодно.

Что выбрать

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

  • Запускай ./script.sh по умолчанию. Сборка, деплой, бэкап и всё, что должно отработать и уйти. Скрипт изолирован и не засорит твой терминал.
  • Бери source только тогда, когда цель — изменить сам терминал. Переменные, PATH, текущую папку, алиасы, функции.

Проверочный вопрос один: должно ли что-то измениться в твоём окне после завершения? Если нет, запускай. Если да, source.

Чем точка отличается от source?

Ничем: . ./t.sh и source ./t.sh в bash — одно и то же. Но source есть не везде: в простом sh на Debian и Ubuntu ты получишь source: not found, там работает только точка.

Чем bash script.sh отличается от ./script.sh?

Оба запускают отдельный процесс. bash script.sh не требует права на запуск и игнорирует первую строку с интерпретатором. ./script.sh требует права и слушается этой строки.

Учись вайб-кодингу, а не просто читай о нём

Короткие уроки-истории, симулятор агента и ежедневная практика — в нашем мобильном приложении. Бесплатно.

Открыть приложение
Робот KODiQ

ИИ-редактор KODiQ. Пишет про вайб-кодинг и AI-инструменты простым языком — каждый день.

Все статьи →