ps-aliases.md — vim

Настройка удобных алиасов в PowerShell для разработчика

Полноценный гайд по настройке PowerShell-профиля: алиасы для пакетных менеджеров, быстрая навигация по проектам, шорткаты для Git, удобные утилиты и приятные мелочи для повседневной работы.


Что такое профиль PowerShell

Профиль PowerShell — это обычный .ps1 скрипт, который автоматически выполняется при каждом запуске оболочки. В него удобно класть:

  • алиасы (короткие имена для длинных команд),
  • функции (произвольные действия в одну команду),
  • настройки промпта, цветов, истории,
  • переменные окружения для сессии.

Существует четыре уровня профилей (от самого общего к самому узкому):

ПеременнаяКто запускаетНазначение
$PROFILE.AllUsersAllHostsВсе пользователи, все хостыСистемные настройки для всех (нужны права админа)
$PROFILE.AllUsersCurrentHostВсе пользователи, текущий хостТолько для PowerShell/ISE/VSCode
$PROFILE.CurrentUserAllHostsТекущий пользователь, все хостыЛичные настройки везде
$PROFILE.CurrentUserCurrentHostТекущий пользователь, текущий хост ($PROFILE)Самый частый вариант

Совет: для личных настроек лучше использовать $PROFILE или CurrentUserAllHosts — не требуют прав администратора и не влияют на других пользователей. AllUsersAllHosts подходит, когда машина точно ваша единственная и нужны одинаковые алиасы для всех хостов сразу.


Как открыть и где лежит

# Узнать путь к нужному профилю
$PROFILE                          # Текущий пользователь, текущий хост
$PROFILE.AllUsersAllHosts         # Глобальный профиль

# Открыть в Блокноте
notepad $PROFILE

# Открыть в VS Code
code $PROFILE

# Создать файл, если его ещё нет
if (!(Test-Path $PROFILE)) { New-Item -Type File -Path $PROFILE -Force }

Типичные пути:

  • AllUsersAllHosts: C:\Program Files\PowerShell\7\profile.ps1 (PS7) или C:\Windows\System32\WindowsPowerShell\v1.0\profile.ps1 (PS5).
  • CurrentUserCurrentHost: C:\Users\<имя>\Documents\PowerShell\Microsoft.PowerShell_profile.ps1.

Базовая настройка

Минимальный рабочий конфиг — алиас для pnpm, функция открытия профиля и быстрый переход в проекты через хэш-таблицу:

Set-Alias -Name p -Value pnpm

function aliasconf { notepad $PROFILE.AllUsersAllHosts }

$projects = @{
    mypet    = "W:\personal\pet-project"
    project2 = "W:\personal\project2"
    wproj    = "W:\work\company\work-project"
}

foreach ($alias in $projects.Keys) {
    $path = $projects[$alias]
    if (-not (Test-Path $path)) {
        Write-Warning "Путь не существует: $path (алиас: $alias)"
        continue
    }
    $funcName = "GoTo_$alias"
    Set-Item -Path "Function:$funcName" -Value { Set-Location $path }.GetNewClosure()
    Set-Alias -Name $alias -Value $funcName
}

Что здесь происходит

  1. Хэш-таблица $projects — единый источник правды. Добавили новый проект — добавили запись в таблицу.
  2. Цикл генерирует функции по именам через Set-Item -Path "Function:$funcName". Имя функции собирается как GoTo_<alias>.
  3. .GetNewClosure() — ключевой момент. Без него все сгенерированные функции захватили бы одну и ту же переменную $path (последнее значение из цикла), и каждый алиас вёл бы в один и тот же проект. Метод создаёт замыкание со «снимком» текущих переменных.
  4. Test-Path заранее отсекает несуществующие пути, чтобы не плодить мёртвые алиасы.
  5. Set-Alias поверх функции — алиас не умеет выполнять блок кода напрямую, но может быть указателем на функцию.

Тонкость: Write-Warning срабатывает при каждом запуске терминала. Если ожидаются временно недоступные пути (например, диск W: подключается по сети), стоит складывать «отвалившиеся» проекты в отдельную переменную и показывать их по запросу — см. раздел Навигация по проектам ниже.


Расширенная конфигурация

1. Алиасы пакетных менеджеров

Set-Alias -Name p   -Value pnpm
Set-Alias -Name y   -Value yarn
Set-Alias -Name n   -Value npm
Set-Alias -Name nx  -Value npx

Для частых скриптов удобнее функции — они принимают аргументы:

function pi    { pnpm install @args }      # pi react react-dom
function pd    { pnpm dev @args }          # запуск dev-сервера
function pb    { pnpm build @args }
function pt    { pnpm test @args }
function pl    { pnpm lint @args }
function prun  { pnpm run @args }          # prun any-script

@args — это сплат всех аргументов функции. Можно писать pi react@19 -D точно так же, как и для pnpm install.

2. Навигация по проектам

Базовый цикл выше уже даёт короткие алиасы (raov2, aksenta и т. д.). Дополним его «тихой» обработкой ошибок и командой goto с автодополнением:

$projectsMissing = @()

foreach ($alias in $projects.Keys) {
    $path = $projects[$alias]
    if (-not (Test-Path $path)) {
        $projectsMissing += [PSCustomObject]@{ Alias = $alias; Path = $path }
        continue
    }
    $funcName = "GoTo_$alias"
    Set-Item -Path "Function:$funcName" -Value { Set-Location $path }.GetNewClosure()
    Set-Alias -Name $alias -Value $funcName
}

# Универсальная команда с автодополнением по Tab
function goto {
    param(
        [ArgumentCompleter({
            param($cmd, $param, $word)
            $projects.Keys | Where-Object { $_ -like "$word*" }
        })]
        [string]$name
    )
    if ($projects.ContainsKey($name)) {
        Set-Location $projects[$name]
    } else {
        Write-Host "Нет проекта '$name'. Доступные: $($projects.Keys -join ', ')" -ForegroundColor Yellow
    }
}

# Список проектов и тех, что не нашлись
function projects         { $projects.GetEnumerator() | Sort-Object Name | Format-Table -AutoSize }
function projects-missing { $projectsMissing | Format-Table -AutoSize }

Использование:

raov2                # короткий алиас (как в базовой настройке)
goto raov2           # универсальная команда
goto ra<Tab>         # автодополнение → raov2 / raop2
projects             # список всех настроенных проектов
projects-missing     # что не удалось примонтировать в этой сессии

Универсальные шорткаты для подъёма по дереву каталогов:

function ..   { Set-Location .. }
function ...  { Set-Location ../.. }
function .... { Set-Location ../../.. }
function ~    { Set-Location $HOME }

3. Git-шорткаты

function gs   { git status @args }
function gco  { git checkout @args }
function gcb  { git checkout -b @args }       # gcb feature/login
function gc   { git commit -m @args }         # gc "fix: typo"
function gca  { git commit -am @args }
function gp   { git push @args }
function gpl  { git pull @args }
function gf   { git fetch --all --prune }
function gl   { git log --oneline --graph --decorate -20 }
function gd   { git diff @args }
function gds  { git diff --staged @args }
function gb   { git branch @args }
function gbd  { git branch -d @args }

# Быстрое переключение на основную ветку (main или master)
function gm {
    $main = (git branch --list main, master | ForEach-Object { $_.Trim('* ') } | Select-Object -First 1)
    if ($main) { git checkout $main } else { Write-Host "main/master не найдены" -ForegroundColor Yellow }
}

# Удалить все смёрженные локальные ветки
function git-clean {
    git branch --merged | Where-Object { $_ -notmatch '^\*|main|master|develop' } | ForEach-Object {
        git branch -d $_.Trim()
    }
}

Внимание: gp в свежем PowerShell — встроенный алиас для Get-ItemProperty. Функция с тем же именем его переопределит (что обычно и нужно), но имейте в виду.

4. Утилиты разработчика

# Открыть текущую папку в проводнике / VS Code
function o   { explorer . }
function c   { code . }

# Очистка кэшей и артефактов node-проекта
function clean-node {
    Get-ChildItem -Path . -Include node_modules, dist, .next, .turbo, .cache -Recurse -Force `
        -ErrorAction SilentlyContinue | Remove-Item -Recurse -Force
    Write-Host "Очищено: node_modules, dist, .next, .turbo, .cache" -ForegroundColor Green
}

# Размер папки
function size {
    param([string]$path = '.')
    $bytes = (Get-ChildItem -Path $path -Recurse -Force -ErrorAction SilentlyContinue |
              Measure-Object -Property Length -Sum).Sum
    "{0:N2} MB" -f ($bytes / 1MB)
}

# Создать папку и сразу перейти
function mkcd {
    param([string]$name)
    New-Item -ItemType Directory -Path $name -Force | Out-Null
    Set-Location $name
}

# Содержимое файла в буфер обмена / из буфера в файл
function clip-out { Get-Content $args[0] | Set-Clipboard }
function clip-in  { Get-Clipboard | Set-Content $args[0] }

5. Работа с портами и процессами

Самое больное место Node-разработки — «адрес уже используется». Решаем одной командой:

# Кто занял порт
function port {
    param([int]$Port)
    Get-NetTCPConnection -LocalPort $Port -ErrorAction SilentlyContinue |
        Select-Object LocalAddress, LocalPort, State, OwningProcess,
            @{N='Process'; E={(Get-Process -Id $_.OwningProcess -ErrorAction SilentlyContinue).Name}}
}

# Убить процесс на конкретном порту
function kill-port {
    param([int]$Port)
    $conn = Get-NetTCPConnection -LocalPort $Port -ErrorAction SilentlyContinue
    if ($conn) {
        $conn.OwningProcess | ForEach-Object {
            Stop-Process -Id $_ -Force
            Write-Host "Процесс $_ на порту $Port остановлен" -ForegroundColor Green
        }
    } else {
        Write-Host "Порт $Port свободен" -ForegroundColor Yellow
    }
}

# Найти процесс по имени и убить
function kill-name {
    param([string]$Name)
    Get-Process -Name $Name -ErrorAction SilentlyContinue | Stop-Process -Force
}

Использование:

port 3000          # посмотреть, кто слушает 3000
kill-port 3000     # освободить порт

6. Управление окружением

# Быстрая правка профиля
function aliasconf { notepad $PROFILE.AllUsersAllHosts }

# Альтернатива без админских прав
function aliasconf-user {
    $path = $PROFILE.CurrentUserAllHosts
    if (-not (Test-Path $path)) { New-Item -Type File -Path $path -Force | Out-Null }
    notepad $path
}

# Перезагрузка профиля без перезапуска терминала
function reload { . $PROFILE.AllUsersAllHosts; Write-Host "Профиль перезагружен" -ForegroundColor Green }

# Показать версии основных инструментов
function versions {
    Write-Host "Node:   " -NoNewline; node --version
    Write-Host "npm:    " -NoNewline; npm --version
    Write-Host "pnpm:   " -NoNewline; pnpm --version 2>$null
    Write-Host "Git:    " -NoNewline; (git --version) -replace 'git version ', ''
    Write-Host "PSh:    " -NoNewline; $PSVersionTable.PSVersion.ToString()
}

7. Красивый промпт и автодополнение

Самое заметное визуальное улучшение — установка oh-my-posh или хотя бы кастомного prompt:

# Простой кастомный промпт с веткой Git
function prompt {
    $path = (Get-Location).Path.Replace($HOME, '~')
    $branch = ''
    if (Test-Path .git) {
        $branch = " ($(git rev-parse --abbrev-ref HEAD 2>$null))"
    }
    Write-Host "PS " -NoNewline -ForegroundColor DarkGray
    Write-Host $path -NoNewline -ForegroundColor Cyan
    Write-Host $branch -NoNewline -ForegroundColor Yellow
    return "> "
}

Автодополнение и подсказки на основе истории (PSReadLine идёт в комплекте с PS7):

Set-PSReadLineOption -PredictionSource HistoryAndPlugin
Set-PSReadLineOption -PredictionViewStyle ListView
Set-PSReadLineOption -EditMode Windows
Set-PSReadLineKeyHandler -Key Tab -Function MenuComplete
Set-PSReadLineKeyHandler -Key UpArrow -Function HistorySearchBackward
Set-PSReadLineKeyHandler -Key DownArrow -Function HistorySearchForward

Для красивого промпта в стиле Powerlevel10k — oh-my-posh:

winget install JanDeDobbeleer.OhMyPosh -s winget
# Потом в профиль:
# oh-my-posh init pwsh --config "$env:POSH_THEMES_PATH\paradox.omp.json" | Invoke-Expression

Полный шаблон профиля

Готовый к копированию файл — кладите в $PROFILE.AllUsersAllHosts (для всех пользователей, требует админа) или в $PROFILE (только для себя):

# ============================================================
#  PowerShell profile — developer setup
# ============================================================

# --- Пакетные менеджеры ---
Set-Alias -Name p   -Value pnpm
Set-Alias -Name y   -Value yarn
Set-Alias -Name n   -Value npm
Set-Alias -Name nx  -Value npx

function pi   { pnpm install @args }
function pd   { pnpm dev @args }
function pb   { pnpm build @args }
function pt   { pnpm test @args }
function pl   { pnpm lint @args }
function prun { pnpm run @args }

# --- Проекты: единая хэш-таблица + цикл ---
$projects = @{
    mypet    = "W:\personal\pet-project"
    project2 = "W:\personal\project2"
    wproj    = "W:\work\company\work-project"
}

$projectsMissing = @()

foreach ($alias in $projects.Keys) {
    $path = $projects[$alias]
    if (-not (Test-Path $path)) {
        $projectsMissing += [PSCustomObject]@{ Alias = $alias; Path = $path }
        continue
    }
    $funcName = "GoTo_$alias"
    Set-Item -Path "Function:$funcName" -Value { Set-Location $path }.GetNewClosure()
    Set-Alias -Name $alias -Value $funcName
}

function goto {
    param(
        [ArgumentCompleter({
            param($cmd, $param, $word)
            $projects.Keys | Where-Object { $_ -like "$word*" }
        })]
        [string]$name
    )
    if ($projects.ContainsKey($name)) { Set-Location $projects[$name] }
    else { Write-Host "Нет проекта '$name'. Доступные: $($projects.Keys -join ', ')" -ForegroundColor Yellow }
}

function projects         { $projects.GetEnumerator() | Sort-Object Name | Format-Table -AutoSize }
function projects-missing { $projectsMissing | Format-Table -AutoSize }

# --- Навигация ---
function ..   { Set-Location .. }
function ...  { Set-Location ../.. }
function .... { Set-Location ../../.. }

# --- Git ---
function gs   { git status @args }
function gco  { git checkout @args }
function gcb  { git checkout -b @args }
function gc   { git commit -m @args }
function gca  { git commit -am @args }
function gp   { git push @args }
function gpl  { git pull @args }
function gf   { git fetch --all --prune }
function gl   { git log --oneline --graph --decorate -20 }
function gd   { git diff @args }
function gds  { git diff --staged @args }
function gb   { git branch @args }

function gm {
    $main = (git branch --list main, master | ForEach-Object { $_.Trim('* ') } | Select-Object -First 1)
    if ($main) { git checkout $main } else { Write-Host "main/master не найдены" -ForegroundColor Yellow }
}

# --- Утилиты ---
function o { explorer . }
function c { code . }

function mkcd {
    param([string]$name)
    New-Item -ItemType Directory -Path $name -Force | Out-Null
    Set-Location $name
}

function clean-node {
    Get-ChildItem -Path . -Include node_modules, dist, .next, .turbo, .cache -Recurse -Force `
        -ErrorAction SilentlyContinue | Remove-Item -Recurse -Force
    Write-Host "Очищено" -ForegroundColor Green
}

# --- Порты ---
function port {
    param([int]$Port)
    Get-NetTCPConnection -LocalPort $Port -ErrorAction SilentlyContinue |
        Select-Object LocalAddress, LocalPort, State, OwningProcess,
            @{N='Process'; E={(Get-Process -Id $_.OwningProcess -ErrorAction SilentlyContinue).Name}}
}

function kill-port {
    param([int]$Port)
    $conn = Get-NetTCPConnection -LocalPort $Port -ErrorAction SilentlyContinue
    if ($conn) {
        $conn.OwningProcess | ForEach-Object { Stop-Process -Id $_ -Force }
        Write-Host "Порт $Port освобождён" -ForegroundColor Green
    } else { Write-Host "Порт $Port свободен" -ForegroundColor Yellow }
}

# --- Управление профилем ---
function aliasconf { notepad $PROFILE.AllUsersAllHosts }
function reload    { . $PROFILE.AllUsersAllHosts; Write-Host "Профиль перезагружен" -ForegroundColor Green }

function versions {
    Write-Host "Node:   " -NoNewline; node --version
    Write-Host "npm:    " -NoNewline; npm --version
    Write-Host "pnpm:   " -NoNewline; pnpm --version 2>$null
    Write-Host "Git:    " -NoNewline; (git --version) -replace 'git version ', ''
    Write-Host "PSh:    " -NoNewline; $PSVersionTable.PSVersion.ToString()
}

# --- PSReadLine: автодополнение и история ---
Set-PSReadLineOption -PredictionSource HistoryAndPlugin
Set-PSReadLineOption -PredictionViewStyle ListView
Set-PSReadLineKeyHandler -Key Tab -Function MenuComplete
Set-PSReadLineKeyHandler -Key UpArrow -Function HistorySearchBackward
Set-PSReadLineKeyHandler -Key DownArrow -Function HistorySearchForward

# --- Промпт ---
function prompt {
    $path = (Get-Location).Path.Replace($HOME, '~')
    $branch = ''
    if (Test-Path .git) {
        $branch = " ($(git rev-parse --abbrev-ref HEAD 2>$null))"
    }
    Write-Host "PS " -NoNewline -ForegroundColor DarkGray
    Write-Host $path -NoNewline -ForegroundColor Cyan
    Write-Host $branch -NoNewline -ForegroundColor Yellow
    return "> "
}

Применение изменений

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

. $PROFILE.AllUsersAllHosts

или, если уже добавлена функция reload:

reload

Частые проблемы

1. «Выполнение сценариев отключено в этой системе»

PowerShell по умолчанию запрещает выполнение .ps1. Разрешите для текущего пользователя:

Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser

2. Все алиасы ведут в один и тот же путь

Классическая ошибка замыкания при динамической генерации функций. Если написать:

Set-Item -Path "Function:$funcName" -Value { Set-Location $path }   # БЕЗ .GetNewClosure()

…то все сгенерированные функции будут ссылаться на одну и ту же переменную $path, которая после цикла равна последнему значению. Решение — .GetNewClosure():

Set-Item -Path "Function:$funcName" -Value { Set-Location $path }.GetNewClosure()

Метод создаёт копию скрипт-блока с захваченным текущим значением переменных.

3. Конфликт имён

Перед добавлением своего алиаса проверьте, не занят ли он:

Get-Alias p -ErrorAction SilentlyContinue
Get-Command p -ErrorAction SilentlyContinue

Особенно стоит проверить двух- и трёхбуквенные имена — в PowerShell много встроенных алиасов (gp, sc, cd, gci и т. д.). Свои функции их перекроют без предупреждения.

4. AllUsersAllHosts требует прав администратора

Файл лежит в Program Files — для записи нужен запуск редактора от имени администратора. Если машина рабочая и общая — используйте $PROFILE.CurrentUserAllHosts, чтобы настройки не зависели от админ-прав и не влияли на других.

5. Write-Warning спамит при каждом запуске

Если в списке $projects встречаются временно недоступные пути (сетевой диск не подключился), Write-Warning будет жёлтым полотном при каждом старте терминала. Аккуратнее — складывать «отвалившиеся» в $projectsMissing и показывать только по команде projects-missing (см. [раздел 2]).

6. Профиль не загружается в VS Code-терминале

VS Code запускает свой хост. Если настройки нужны и там — используйте $PROFILE.CurrentUserAllHosts (применяется ко всем хостам) или $PROFILE.AllUsersAllHosts.


Готово. Скопируйте нужные блоки в свой профиль, перезагрузите PowerShell — и работайте быстрее.