sh
POSIX-совместимая командная оболочка — переносимый интерпретатор стандарта IEEE POSIX.1
Описание
sh — стандартная командная оболочка POSIX (IEEE Std 1003.1), определённая как минимальный обязательный интерфейс командной строки для совместимых Unix-систем. Исторически sh восходит к оболочке Стивена Борна (Bourne Shell, 1979), реализованной в System 7 Unix. В современных Linux-системах /bin/sh является симлинком на dash (Debian Almquist Shell) — быстрый и легковесный интерпретатор, строго следующий стандарту POSIX.
Основное назначение sh — написание переносимых скриптов, которые работают на любой POSIX-совместимой системе: Linux, macOS, BSD, Solaris, AIX. Скрипты инициализации системы (/etc/init.d/), пакетные скрипты Debian (maintainer scripts), и Makefile-цели часто используют sh намеренно — чтобы не зависеть от конкретной оболочки.
Используй sh когда переносимость важнее удобства: системные скрипты, Docker-образы с минимальным окружением (где bash может не быть), CI/CD-пайплайны, Makefile, пакетные скрипты. Избегай bash-специфичных возможностей (массивы, [[, local в функциях, brace expansion) — всё это не POSIX.
Синтаксис
- OPTIONS — флаги поведения оболочки (практически совпадают с bash, но без расширений)
- SCRIPT — путь к файлу скрипта; если не указан, запускается интерактивная оболочка
- ARGUMENTS — позиционные параметры
$1,$2, …,$@ - -c "COMMAND_STRING" — выполнить строку как команду без файла
- -s — читать команды из stdin, принимать позиционные параметры из аргументов
Флаги и опции
| Флаг | Описание |
|---|---|
-c "cmd" | Выполнить строку cmd как команду. Пример: sh -c "echo \$PATH" |
-s | Читать команды из stdin. Позиционные параметры задаются через аргументы командной строки |
-e | Завершить скрипт при первой ошибке (ненулевой код завершения). POSIX-эквивалент bash -e |
-u | Трактовать обращение к неустановленной переменной как ошибку (POSIX nounset) |
-x | Режим трассировки: печатать каждую команду перед выполнением с префиксом + |
-n | Проверить синтаксис без выполнения (noexec / dry run) |
-v | Verbose: печатать каждую строку скрипта по мере чтения, до расширения переменных |
-f | Отключить glob-подстановку (filename generation). Символы *, ?, [ воспринимаются буквально |
-a | Автоматически экспортировать все переменные в окружение дочерних процессов (allexport) |
-i | Запустить как интерактивную оболочку (игнорировать некоторые сигналы, показывать prompt) |
-m | Включить job control (monitor mode) — управление фоновыми процессами |
-- | Конец опций. Следующий аргумент воспринимается как имя скрипта, даже если начинается с - |
Паттерны использования
Переносимый POSIX-скрипт
#!/bin/sh
# Переносимый shebang — POSIX sh, не bash
set -eu
NAME="${1:-World}"
echo "Hello, ${NAME}!"
# POSIX: [ ] вместо bash [[ ]]
if [ -f /etc/os-release ]; then
. /etc/os-release
echo "OS: ${NAME}"
fi
Функции в POSIX sh
#!/bin/sh
set -eu
# Объявление функции (POSIX-синтаксис)
log() {
printf '[%s] %s\n' "$(date '+%H:%M:%S')" "$*"
}
die() {
log "ERROR: $*" >&2
exit 1
}
log "Script started"
[ -d /tmp ] || die "/tmp not found"
log "Done"
Передача аргументов в Docker CMD
# Dockerfile: sh -c для передачи переменных окружения CMD ["/bin/sh", "-c", "exec myapp --port $PORT"] # Или в entrypoint-скрипте: #!/bin/sh set -e exec "$@" # В Makefile (sh по умолчанию) deploy: sh scripts/deploy.sh $(ENV)
Проверка переменных и значения по умолчанию
#!/bin/sh
# POSIX parameter expansion
# Значение по умолчанию если не задана
PORT="${PORT:-8080}"
# Ошибка если не задана
: "${DATABASE_URL:?DATABASE_URL must be set}"
# Присвоить и вернуть если пусто
TMPDIR="${TMPDIR:=/tmp}"
echo "Port: $PORT"
echo "TMP: $TMPDIR"
Проверка синтаксиса скрипта
# Проверить скрипт (sh версия) sh -n script.sh # Трассировка выполнения sh -x script.sh arg1 arg2 # Комбинация: verbose + trace sh -vx script.sh # Запуск с переопределением переменной PORT=9090 sh script.sh
Heredoc и многострочный ввод
#!/bin/sh # Heredoc — POSIX-совместим cat <<'EOF' This is a literal heredoc. Variables like $HOME are not expanded. EOF cat <
Советы и предупреждения
На Ubuntu/Debian
/bin/sh — это dash, строго следующий POSIX. Тестируй переносимые скрипты явно через dash script.sh, чтобы убедиться в отсутствии bash-измов. На macOS /bin/sh — ограниченная версия bash, что может маскировать несовместимость.
Поведение
echo с escape-последовательностями (\n, \t) различается между оболочками и платформами. Для переносимости всегда используй printf '%s\n' "$var" — это POSIX-стандарт с предсказуемым поведением.
Ключевое слово
local для локальных переменных в функциях — не часть POSIX, хотя поддерживается большинством оболочек на практике. Для строго переносимого кода используй субоболочки: (subshell_var=value; cmd). Также не являются POSIX: массивы, [[, $(( )) расширенная арифметика, source (используй .).
Если скрипт использует
bash-специфичные возможности (ассоциативные массивы, [[ ]], mapfile, brace expansion {a..z}), указывай shebang #!/usr/bin/env bash. Запуск bash-скрипта через sh на системах где sh=dash приводит к непредсказуемым ошибкам.