Powershell in My Toybox

Powershellのおもちゃ箱ひっくり返してみた。

##特殊コマンドの一覧

*explorer.exe*では、**shell:xxxxx**をパス欄に入力することで特殊フォルダを表示してくれます。
ただ、「どんなコマンドたっけな？」となることが稀によくあり、その都度ググるのがメンドーになったので、コマンドレット化した。

```powershell
$categories = @(
  [PSCustomObject]@{Name='Menu'; Description='メニュー関連の項目'}
  [PSCustomObject]@{Name='User'; Description='ユーザーフォルダー関連の項目'}
  [PSCustomObject]@{Name='Public'; Description='パブリックフォルダー関連の項目'}
  [PSCustomObject]@{Name='Library'; Description='ライブラリフォルダー関連の項目'}
  [PSCustomObject]@{Name='System'; Description='システム関連の項目'}
) 

$menuCommands = @(
  [PSCustomObject]@{Command='shell:Start Menu'; Description='現ユーザーのスタート メニュー'}
  [PSCustomObject]@{Command='shell:Programs'; Description='現ユーザーのプログラムフォルダー'}
  [PSCustomObject]@{Command='shell:Startup'; Description='現ユーザーのスタートアップフォルダー'}
  [PSCustomObject]@{Command='shell:Administrative Tools'; Description='現ユーザーの管理ツールフォルダー'}
  [PSCustomObject]@{Command='shell:Common Start Menu'; Description='全ユーザのスタート メニュー'}
  [PSCustomObject]@{Command='shell:Common Programs'; Description='全ユーザーのプログラムフォルダー'}
  [PSCustomObject]@{Command='shell:Common Startup'; Description='全ユーザーのスタートアップフォルダー'}
  [PSCustomObject]@{Command='shell:Common Administrative Tools'; Description='全ユーザーの管理ツールフォルダー'}
)
$userCommands = @(
  [PSCustomObject]@{Command='shell:UserProfiles'; Description='ユーザールート'}
  [PSCustomObject]@{Command='shell:Profile'; Description='ユーザープロファイルルート'; Alias=@('shell:UsersFilesFolder')}
  [PSCustomObject]@{Command='shell:Desktop'; Description='デスクトップ'}
  [PSCustomObject]@{Command='shell:SendTo'; Description='送るメニュー'}
  [PSCustomObject]@{Command='shell:Recent'; Description='最近使った項目'}
  [PSCustomObject]@{Command='shell:Games'; Description='ゲーム'}
  [PSCustomObject]@{Command='shell:MyComputerFolder'; Description='コンピューターフォルダー'}
  [PSCustomObject]@{Command='shell:RecycleBinFolder'; Description='ごみ箱'}
  [PSCustomObject]@{Command='shell:HomeGroupFolder'; Description='ホームグループフォルダー'}
  [PSCustomObject]@{Command='shell:NetworkPlacesFolder'; Description='ネットワークフォルダー'}
  [PSCustomObject]@{Command='shell:Searches'; Description='検索フォルダー'}
  [PSCustomObject]@{Command='shell:SearchHomeFolder'; Description='検索結果フォルダー'}
  [PSCustomObject]@{Command='shell:Personal'; Description='現ユーザーのドキュメントフォルダー'}
  [PSCustomObject]@{Command='shell:My Pictures'; Description='現ユーザーのピクチャフォルダー'}
  [PSCustomObject]@{Command='shell:My Music'; Description='現ユーザーのミュージックフォルダー'}
  [PSCustomObject]@{Command='shell:My Video'; Description='現ユーザーのビデオフォルダー'}
  [PSCustomObject]@{Command='shell:Contacts'; Description='現ユーザーのアドレス帳フォルダー'}
  [PSCustomObject]@{Command='shell:Downloads'; Description='現ユーザーのダウンロードフォルダー'}
  [PSCustomObject]@{Command='shell:Favorites'; Description='現ユーザーのお気に入りフォルダー'}
  [PSCustomObject]@{Command='shell:Cookies'; Description='現ユーザーのCookieフォルダー'}
  [PSCustomObject]@{Command='shell:History'; Description='現ユーザーのWebブラウザー履歴フォルダー'}
  [PSCustomObject]@{Command='shell:Cache'; Description='現ユーザーの一時ファイルフォルダー'}
  [PSCustomObject]@{Command='shell:Links'; Description='現ユーザーのリンクフォルダー'}
  [PSCustomObject]@{Command='shell:Templates'; Description='現ユーザーのテンプレートフォルダー'}
  [PSCustomObject]@{Command='shell:CD Burning'; Description='現ユーザーのCD／DVDの一時書き込み用フォルダー'}
  [PSCustomObject]@{Command='shell:NetHood'; Description='現ユーザーのネットワークショートカットフォルダー'}
  [PSCustomObject]@{Command='shell:PrintHood'; Description='現ユーザーのプリンターショートカットフォルダー'}
  [PSCustomObject]@{Command='shell:Quick Launch'; Description='現ユーザーのクイック起動フォルダー'}
  [PSCustomObject]@{Command='shell:Common Templates'; Description='全ユーザーのテンプレートフォルダー'}
)
	 	

$publicUserCommands = @(
  [PSCustomObject]@{Command='shell:Public'; Description='ルートフォルダ'}
  [PSCustomObject]@{Command='shell:Common Documents'; Description='ドキュメントフォルダー'}
  [PSCustomObject]@{Command='shell:Common Desktop'; Description='デスクトップフォルダー'}
  [PSCustomObject]@{Command='shell:CommonPictures'; Description='ピクチャフォルダー'}
  [PSCustomObject]@{Command='shell:CommonMusic'; Description='ミュージックフォルダー'}
  [PSCustomObject]@{Command='shell:CommonVideo'; Description='ビデオフォルダー'}
  [PSCustomObject]@{Command='shell:CommonDownloads'; Description='ダウンロードフォルダー'}
)

$libraryCommands = @(
  [PSCustomObject]@{Command='shell:Libraries'; Description='ルートフォルダ'; Alias=@('shell:UsersLibrariesFolder')}
  [PSCustomObject]@{Command='shell:DocumentsLibrary'; Description='ドキュメントフォルダー'}
  [PSCustomObject]@{Command='shell:PicturesLibrary'; Description='ピクチャフォルダー'}
  [PSCustomObject]@{Command='shell:MusicLibrary'; Description='ミュージックフォルダー'}
  [PSCustomObject]@{Command='shell:VideosLibrary'; Description='ビデオフォルダー'}
)

$systemCommands = @(
  [PSCustomObject]@{Command='shell:ControlPanelFolder'; Description='コントロール '}
  [PSCustomObject]@{Command='shell:PrintersFolder'; Description='プリンターフォルダー'}
  [PSCustomObject]@{Command='shell:ConnectionsFolder'; Description='ネットワーク接続フォルダー'}
  [PSCustomObject]@{Command='shell:SyncCenterFolder'; Description='同期センターフォルダー'}
  [PSCustomObject]@{Command='shell:Windows'; Description='システムルート'}
  [PSCustomObject]@{Command='shell:Fonts'; Description='フォントフォルダー'}
  [PSCustomObject]@{Command='shell:System'; Description='システムフォルダー'}
  [PSCustomObject]@{Command='shell:InternetFolder'; Description='Internet Explorerの起動'}
)

$lookup = @{
  Menu = $menuCommands
  User = $userCommands
  Public = $publicUserCommands 
  Library = $libraryCommands
  System = $systemCommands
}

function Get-ShellCommandCategory {
  foreach ($cat in $categories) {
    $cat
  }
}

function Get-ShellCommand {
  [CmdletBinding()]
  param(
    [string[]]$Category = ($categories | %{ $_.Name })
  )
  
  foreach ($cat in $Category) {
    foreach ($c in $lookup[$cat]) {
      [PSCustomObject]@{
        Command = $c.Command; Description = $c.Description; Category = $cat
      }
    }
  }
}
```

数多く用意されているので、テキトーに分類。分類の一覧は**Get-ShellCommandCategory**で列挙。

コマンドの一覧は、**Get-ShellCommand**を使用する。

個人的には、

* shell:Programs
    * スタートメニューからの検索のためにプログラムのショートカットを登録目的 
* shell:SendTo
    * コンテキストメニューの送るにショートカットを追加する目的

よく使う。

##MacOSのopenコマンドっぽい何か

MacOSに用意されたopenコマンドは、

* アプリケーションを指定
    * アプリケーションを実行する
* ファイルを指定
    * 関連付けられたアプリで開く
* urlを指定
    * ブラウザで開いてくれる

という、ものぐさにやさしい（？）ものとなっています。

これ、powershellにも欲しいとなって、作ったのが以下

```powershell
function open {
  [CmdletBinding()]
  param (
    [Parameter(ValueFromPipeline=$true)]
    [string[]]$Path = @("$pwd/")
  )
  process {
    foreach ($p in $Path) {
      if ($p.ToLower() -match '^(shell|http|https):') {
        Write-Verbose "Invoke by explorer.exe $p"
        & explorer $p
      }
      elseif (([IO.Path]::IsPathRooted($p)) -and (New-Object Uri $p).IsUnc) {
        Write-Verbose "Invoke by explorer.exe $p"
        & explorer $p
      }
      else {
        Write-Verbose "call Invoke-Item $p"
        invoke-Item $p
      }
    }
  }
}
```

中身はなんてことはなく、

* パスがhttp, https, shellで始まる、またはUNCフルパスなら、explorer.exeを実行。

* それ以外は、**Invoke-Item**を実行

またずぼらさをさらに加速させるために、引数がない場合はカレントディレクトリを開くおまけつき。

起動プロファイル($PROFILE)に突っ込んで常用してる。

##シンボリックリンク作成

Windows10のCreators Update以降、開発者モードにしていれば、管理者昇格なしにシンボリックリンクが作成(mklink)できるようになっている。
しかし

「だが、Powershellテメーはだめだ！」

状態（つらい）

しゃーないのでコマンドレット化

```powershell
function New-Symlink {
  [CmdletBinding()]
  param (
    [Parameter(Mandatory=$true)]
    [string]$Name,
    
    [Parameter(Mandatory=$true)]
    [string]$SourceName
  )
  
  if (-not ([IO.Path]::IsPathRooted($SourceName))) {
    $SourceName = [IO.Path]::Combine($pwd, $SourceName)
  }
  
  $opt = if ([IO.Directory]::Exists($SourceName)) { "/D" } else { "" }
  
  $command = '"mklink {0} {1} {2}"' -F $opt, $Name, $SourceName
  
  & cmd "/C" $command
}
```
単にmklinkを呼んでるだけ（つらい）

##まとめ

明日から使えるかもしれないカスタムコマンドをいくつか蔵出ししました。
おもちゃ箱をさらに引っ掻き回せば、他にも見つかるかもですがそれはまたいずれかくかも（書くとは言っていない）