場所スタック関係のややこしい仕様

ややこしいので整理。

pushdなどで用いられる場所スタック(location stack)には無名スタック(unnamed location stack)と名前つきスタック(named location stack)があるが、それとは別に「既定スタック」(default location stack)という概念もある。たとえば、pushdとするとデフォルトでは無名スタックに現在のロケーションが追加されるが、それはデフォルトでは無名スタックが既定スタックになっているから。既定スタック以外のスタックに追加するには、pushd -s a (ちゃんと書くとPush-Location -StackName a)のようにスタック名を指定する。ここでは名前つきスタックaに追加している。

さて任意の場所スタックxは Set-Location -StackName x (sl -s x と簡単に書ける)で既定スタックにできる。xが既定スタックになると、pushdとするだけでスタックxに追加されるようになる。

実はそれだけではなく、既定スタックと無名スタックの内容は同期される。つまり、既定スタックに追加すると無名スタックにも自動的に追加され、無名スタックに追加すると既定スタックにも自動的に追加される。具体的な動作を以下に示す。

system32> gl -StackName a # この状態から開始する。スタックaの中身はこのとおり。( Get-Location -StackName a でスタックaの中身を表示できる。
ちなみに、Get-Location -Stack という既定スタックの中身を表示する構文もあるので、-StackNameというパラメータを-sと簡略化することはできない)

Path                                                                                                                                 
----                                                                                                                                 
C:\Windows\system32                                                                                                                  

system32> gl -StackName '' # 無名スタックの中身はこのとおり。現在無名スタックが既定スタックである。(無名スタックは空文字列で指定する)

Path                                                                                                                                 
----                                                                                                                                 
C:\Windows                             

system32> sl -s a # スタックaを既定スタックにする

system32> gl -StackName a # スタックaの中身はもちろん変わらない

Path                                                                                                                                 
----                                                                                                                                 
C:\Windows\system32                                                                                                                  

system32> gl -StackName '' # 無名スタックに入っていた中身が消えて、代わりにスタックaと同じ中身になっている。

Path                                                                                                                                 
----                                                                                                                                 
C:\Windows\system32                  

system32> cd c:\users

users> pushd -s a # スタックaに追加すると・・・

users> gl -StackName '' # 無名スタックにも追加され、

Path                                                                                                                                 
----                                                                                                                                 
C:\users                                                                                                                             
C:\WINDOWS\system32                                                                                                                  

users> cd '..\Program Files'

Program Files> pushd -s '' # 無名スタックに追加すると・・・

Program Files> gl -StackName a # スタックaにも追加される。

Path                                                                                                                                 
----                                                                                                                                 
C:\Program Files                                                                                                                     
C:\users                                                                                                                             
C:\WINDOWS\system32           

以上のように、既定スタックを名前つきスタックaにすると、無名スタックとスタックaは、
・両者とも名前つきスタックaの内容になる。無名スタックの側が名前つきスタックに合わせるともいえる。
・両者に加えられた変更は同期される。上の例ではpushdだったけれどもpopdでも同様。
となる。

紛らわしいことに、同期により無名スタックの中身が上書きされて失われるわけでは"ない"。もとあった内容は既定スタックを無名スタックに戻すと回復する。すなわち次のようになる。

WINDOWS> gl -StackName '' # 既定スタックは無名スタックで、スタックの中身はこのとおり。

Path                                                                                                                                 
----                                                                                                                                 
C:\WINDOWS\system32                                                                                                                  

WINDOWS> gl -StackName a # スタックaの中身。

Path                                                                                                                                 
----                                                                                                                                 
C:\WINDOWS                                                                                                                           

WINDOWS> sl -s a # 既定スタックをaに。

WINDOWS> gl -StackName '' # 無名スタックのもとの内容が隠れて見えなくなる。

Path                                                                                                                                 
----                                                                                                                                 
C:\WINDOWS                                                                                                                           

WINDOWS> gl -StackName a

Path                                                                                                                                 
----                                                                                                                                 
C:\WINDOWS                                                                                                                           

WINDOWS> sl -s '' # 既定スタックを無名スタックに戻す。

WINDOWS> gl -StackName '' # もとの内容が回復する。

Path                                                                                                                                 
----                                                                                                                                 
C:\WINDOWS\system32                                                                                                                  

WINDOWS> gl -StackName a

Path                                                                                                                                 
----                                                                                                                                 
C:\WINDOWS             

つまり、既定スタックを無名スタックにすると、
・無名スタックの内容がもとの内容に戻る。詳しく言うと、最後に無名スタックが既定スタックだった時の内容に戻る。
・直前に既定スタックだった名前つきスタックの内容は変わらない。
となる。

これまで無名スタックと既定スタックの挙動を見てきたけれども、スタックに関して他に気になる点、注意したい点を挙げておく。

まず、全てのスタックの名前を取得する方法が見当たらないこと。それができれば、全てのスタックの中身を表示する便利関数が書けるのに。

次は、既定スタックがどれかを取得する方法も見当たらないこと。既定スタックのsetはできるけどgetする方法がどうも用意されていないっぽい。

それから、名前つきスタックは中身が無くなるとスタック自体が削除されること。スタック自体が削除されても同名のスタックにpushdで追加することはできる。その場合はスタックが新規作成されるので問題ない。しかし、Get-Location -StackName foo でスタックの中身を取得しようとするとエラーになるから注意が必要だ。

引数 "stackName" の値が無効なため、引数を処理できません。引数 "stackName" の値を変更し、操作を実行し直してください。

例えば、pushdのラッパー関数としてスタックに既にあるロケーションは追加しない関数を書くとき、スタックとの重複をチェックする部分のエラー処理に注意が必要だろう。

最後は、pushd <パス名> の挙動。これはそのパスがスタックに追加されるわけではなく、カレントロケーションが追加されてそのパスにカレントが移動する。

以上、場所スタック関連は少し要注意な挙動が多くて慣れるまでに時間がかかりそうだ。