読者です 読者をやめる 読者になる 読者になる

PowerShellのprofileを整理してpromptをいい感じにした

f:id:nanakira:20170129143649j:plain

tl;dr

  • PowerShell の profile を分割して dotfiles 管理にした
  • カレントフォルダパスが長くてもいい感じに見やすいプロンプトにした

動機

Windows のシェル環境はろくにカスタマイズもせず PowerShell を使ってたんだけど、 カレントディレクトリが深くなってくるとプロンプトがどんどん長くなっていって見づらい。 コマンドの途中で折り返されたりしてツライ。

カスタマイズしようにも、profile を雑に継ぎ足してきたので、どこに手を入れたらいいか分からない。

さらに、どうせいじるなら複数環境で同じものを再現できるようにしたい。

と、以上から重い腰を上げて profile の整理に着手した。

ちなみに ConEmu + PowerShell v5 + posh-git + Chocolatey の組み合わせで使っている。

今の profile を分割管理にする

まず、今の profile はごちゃごちゃしていて見通しが悪いので、Emacs の init-loader みたいに分割管理しようと思った。

ざっと調べたところ、id:guitarrapc_tech さんのこちらの記事に全て書いてあった。

PowerShell で Profile を利用して スクリプトの自動読み込みをしてみよう

非常に詳しく説明されており大変参考になった。

Profile の場所確認

PowerShell 上で

> $profile

するとわかる。 が、これで表示されるのは CurrentUserCurrentHost のものなので、 PowerShellPowerShell ISE 共通で読み込ませたい場合は CurrentUserAllHosts として profile.ps1 に記載する方がよい。

if (-not(Test-Path $profile.CurrentUserAllHosts))
{
    New-Item -Path $profile.CurrentUserAllHosts -ItemType file
}

で profile.ps1 が作成される。

profile.ps1 からファイルを読み込ませる

guitarrapc_tech さんの方法を少し簡略化して、profile.ps1 を下記のようにした。

$psdir="$env:HOME\dotfiles\PowerShell"
Write-Host ("Load PS Profiles from {0}\autoload" -f $psdir) -ForegroundColor DarkCyan
Get-ChildItem $psdir\autoload | where Extension -eq ".ps1" | %{.$_.FullName}

これで $HOME/dotfiles/autoload 配下に置いた .ps1 が読み込まれる。

あとは、元の profile の内容を適宜分割して、autoload 配下に入れてやれば OK。

プロンプトをいい感じにする

プロンプトを改造する記事はいくつか見つかるものの、やりたいことにハマるものがないなー、と探してたら、 posh-gitREADME にまさにやりたいことが書いてあった。 README はちゃんと読もう。。

ちなみに、posh-git インストール直後は posh-git インストールフォルダの profile.example.ps1 を参照するようになってると思うが、 中身は

  • Import-Module posh-git
  • prompt のフォーマット定義
  • SSH Agent 起動

なので autoload にいい感じに移動して良い。 その prompt のフォーマット部分をいじる。

カレントパス(+ Git の status)とプロンプトの間に改行を入れる

Import-Module posh-git
$global:GitPromptSettings.BeforeText = '['
$global:GitPromptSettings.AfterText  = '] '
function prompt {
    $origLastExitCode = $LASTEXITCODE
    Write-VcsStatus
    Write-Host $ExecutionContext.SessionState.Path.CurrentLocation -ForegroundColor Green
    $LASTEXITCODE = $origLastExitCode
    "$('>' * ($nestedPromptLevel + 1)) "
}

Write-Host に -NoNewline をつけなければ、改行される。

$global:GitPromptSettings.BeforeText あたりは設定の微調整。README 参照。

パスに $HOME が含まれていたら ~ に置き換えてスッキリさせる

    $curPath = $ExecutionContext.SessionState.Path.CurrentLocation.Path
    if ($curPath.ToLower().StartsWith($Home.ToLower()))
    {
        $curPath = "~" + $curPath.SubString($Home.Length)
    }

    Write-Host $curPath -ForegroundColor Green

パスが長すぎたら省略する

    $maxPathLength = 40
    $curPath = $ExecutionContext.SessionState.Path.CurrentLocation.Path
    if ($curPath.Length -gt $maxPathLength) {
        $curPath = '...' + $curPath.SubString($curPath.Length - $maxPathLength + 3)
    }

最終形

以上をふまえて、最終的にこんな感じになった。

function global:prompt {
    $origLASTEXITCODE = $LASTEXITCODE
    Write-VcsStatus

    # Replace $home by ~ if current path includes $home
    $curPath = $ExecutionContext.SessionState.Path.CurrentLocation.Path
    if ($curPath.ToLower().StartsWith($Home.ToLower()))
    {
        $curPath = "~" + $curPath.SubString($Home.Length)
    }

    # Limit path length up to maxPathLength
    $maxPathLength = 40
    if ($curPath.Length -gt $maxPathLength) {
        $curPath = '...' + $curPath.SubString($curPath.Length - $maxPathLength + 3)
    }

    Write-Host $curPath -ForegroundColor DarkGreen
    $global:LASTEXITCODE = $origLASTEXITCODE

    if ($?) {
        Write-Host " (*'-')"  -NoNewLine -ForegroundColor "DarkGreen"
    } else {
        Write-Host " (*;-;)"  -NoNewLine -ForegroundColor "Red"
    }
    return "> "
}

見た目はこんな感じで、だいぶ気に入っている。 f:id:nanakira:20170129142121j:plain

最後にかわいくした部分は Qiita の こちらの記事 を参照させてもらった。