※ この記事は 2022年5月 に作成したものを一部改稿したものです。
PowerShell は主に Windows のシステム管理や自動化のために開発されたコマンドラインインターフェースおよびスクリプト言語で、Windows 7 から標準で提供されています。
Windows には PowerShell の登場以前から コマンドプロンプト (cmd.exe) が搭載されていますが、GUIで提供されるような管理機能の自動化や再利用可能なスクリプトの作成には不向きで、PowerShell はこうした課題を解決するものとして開発されました。
2016年にはオープンソース&クロスプラットフォーム化した PowerShell Core が発表され、Linux や Mac でも利用できるようになりましたが、今回は Windows で提供されている Windows PowerShell をターゲットとし、快適に利用するためのカスタマイズ方法について考察します。
筆者の環境の Windows PowerShell のバージョンは以下の通りです。
PS C:\Users\xxx > $PSVersionTable
Name Value
---- -----
PSVersion 5.1.19041.1682
PSEdition Desktop
PSCompatibleVersions {1.0, 2.0, 3.0, 4.0...}
BuildVersion 10.0.19041.1682
CLRVersion 4.0.30319.42000
WSManStackVersion 3.0
PSRemotingProtocolVersion 2.3
SerializationVersion 1.1.0.1
PowerShell スクリプトの実行
本題に入る前に、まずは PowerShell スクリプトの実行方法について確認しておきます。
以下のコードをコピーし、hello.ps1
などの名前で保存します。(.ps1
は PowerShell スクリプトに用いられる拡張子です。)
Write-Host 'Hello, world!'
Windows PowerShell を起動し、cd
コマンドで hello.ps1
があるディレクトリに移動します。
PS C:\Users\xxx> cd .\sandbox\
PS C:\Users\xxx\sandbox> Get-ChildItem
ディレクトリ: C:\Users\xxx\sandbox
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 2022/05/27 18:31 27 hello.ps1
ディレクトリを移動したら、以下のようにして hello.ps1
を実行します。
PS C:\Users\xxx\sandbox> .\hello.ps1
.\hello.ps1 : このシステムではスクリプトの実行が無効になっているため、ファイル C:\Users\xxx\sandbox\hello.ps1 を読み込むことができません。詳細については、「about_Execution_Policies」(https://go.microsoft.com/fwlink/?LinkID=135170) を参照してください。
発生場所 行:1 文字:1
+ .\hello.ps1
+ ~~~~~~~~~~~
+ CategoryInfo : セキュリティ エラー: (: ) []、PSSecurityException
+ FullyQualifiedErrorId : UnauthorizedAccess
赤文字で上記のようなエラーメッセージが表示され、実行に失敗した方が多いかもしれません。
PowerShell は強力なツールであり、悪意のあるスクリプトの実行を防ぐため、Windows のクライアントPCではデフォルトではスクリプトの実行を許可しない設定になっています。
スクリプトの実行を許可するには、Windows PowerShell を管理者権限で立ち上げて以下のコマンドを実行し、実行ポリシーを変更します。
(この変更は元に戻すことが可能です。)
PS C:\Windows\system32> Set-ExecutionPolicy RemoteSigned
実行ポリシーの変更
実行ポリシーは、信頼されていないスクリプトからの保護に役立ちます。実行ポリシーを変更すると、about_Execution_Policies のヘルプトピック (https://go.microsoft.com/fwlink/?LinkID=135170) で説明されているセキュリティ上の危険にさらされる可能性があります。実行ポリシーを変更しますか?
[Y] はい(Y) [A] すべて続行(A) [N] いいえ(N) [L] すべて無視(L) [S] 中断(S) [?] ヘルプ (既定値は "N"): Y
RemoteSigned
は、「インターネットからダウンロードしたスクリプトを実行するには信頼できる発行元からのデジタル署名が必要だが、ローカルPC上で作成したスクリプトは署名を必要としない」という実行ポリシーです。
確認のプロンプトが表示されたら、「Y」を入力して [Enter] を押します。
Get-ExecutionPolicy
を実行し、RemoteSigned
と表示されれば正常に実行ポリシーを変更できています。
PS C:\Windows\system32> Get-ExecutionPolicy
RemoteSigned
元のシェルに戻り、再度 hello.ps1
を実行します。
PS C:\Users\xxx\sandbox> .\hello.ps1
Hello, world!
「Hello, world!」と表示されればOKです。
PowerShell プロファイルの作成
ここからが本題です。
Linux などで Bash を使用している方の中には、ホームディレクトリに .bash_profile
や .bashrc
などのファイルを作成して環境変数やエイリアス (別名) の設定をしたことがある方が多いのではないでしょうか。(Zsh だと .zprofile
と .zshrc
)
PowerShell でも、同様に開始時に読み込まれる PowerShell プロファイルを作成して、エイリアスの設定や関数の定義などをすることが可能です。
現在のユーザでログオン時に読み込まれるプロファイルのパスは、変数 $PROFILE
で確認することができます。
PS C:\Users\xxx\sandbox> $PROFILE
C:\Users\xxx\Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1
以下のコードをコピー&ペーストして実行すると、プロファイルが作成されます。
if (!(Test-Path -Path $PROFILE)) {
New-Item -ItemType File -Path $PROFILE -Force
}
PS C:\Users\xxx\sandbox> if (!(Test-Path -Path $PROFILE)) {
>> New-Item -ItemType File -Path $PROFILE -Force
>> }
ディレクトリ: C:\Users\xxx\Documents\WindowsPowerShell
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 2022/05/27 19:58 0 Microsoft.PowerShell_profile.ps1
このファイルを編集することで、PowerShell を自分好みにカスタマイズすることができます。
PowerShell プロファイルの設定例
2022/05/27 現在の私の PowerShell プロファイルは、以下の内容となっています。
function Edit-Hosts {
Start-Process `
-FilePath wsl.exe `
-ArgumentList 'vim /mnt/c/Windows/System32/drivers/etc/hosts' `
-Verb RunAs
}
function Get-EnvironmentVariables {
[Environment]::GetEnvironmentVariables().GetEnumerator() | Sort-Object -Property Key
}
function Edit-EnvironmentVariables {
Start-Process `
-FilePath rundll32.exe `
-ArgumentList 'sysdm.cpl,EditEnvironmentVariables' `
-Verb RunAs
}
function Export-JavaHome ([int] $version) {
switch ($version) {
8 {
$dir = 'C:\langs\java\jdk8u312-b07'
}
11 {
$dir = 'C:\langs\java\jdk-11.0.14.101-hotspot'
}
17 {
$dir = 'C:\langs\java\jdk-17.0.2.8-hotspot'
}
default {
Write-Host 'Error: Invalid Java version'
return
}
}
Start-Process `
-FilePath powershell.exe `
-ArgumentList "-Command [Environment]::SetEnvironmentVariable('JAVA_HOME', '${dir}', [EnvironmentVariableTarget]::Machine)" `
-Verb RunAs
}
function prompt {
$principal = [Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()
if ($principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
$role = 'Admin'
} else {
$role = 'User'
}
'[{0}] {1} > ' -f $role, ((Get-Location).Path -split '::')[-1]
}
Set-Alias hosts Edit-Hosts
Set-Alias env Edit-EnvironmentVariables
Set-PSReadlineKeyHandler -Chord Alt+b -Function BackwardWord
Set-PSReadlineKeyHandler -Chord Alt+d -Function KillWord
Set-PSReadlineKeyHandler -Chord Alt+f -Function ForwardWord
Set-PSReadlineKeyHandler -Chord Ctrl+a -Function BeginningOfLine
Set-PSReadlineKeyHandler -Chord Ctrl+d -Function DeleteCharOrExit
Set-PSReadlineKeyHandler -Chord Ctrl+e -Function EndOfLine
Set-PSReadlineKeyHandler -Chord Ctrl+k -Function KillLine
Set-PSReadlineKeyHandler -Chord Ctrl+u -Function BackwardKillLine
Set-PSReadlineKeyHandler -Chord Ctrl+w -Function BackwardKillWord
Set-PSReadlineKeyHandler -Chord Ctrl+Insert -Function Copy
Set-PSReadLineKeyHandler -Chord Tab -Function Complete
PowerShell の文法などにも触れつつ、順に見ていきたいと思います。
Edit-Hosts
function Edit-Hosts {
Start-Process `
-FilePath wsl.exe `
-ArgumentList 'vim /mnt/c/Windows/System32/drivers/etc/hosts' `
-Verb RunAs
}
まずは、Edit-Hosts
です。
こちらは名前解決で用いる hosts
ファイルを編集するための関数で、PowerShell に元々存在する関数ではなく、独自に定義した関数になります。
関数を定義するには function
キーワードを使用します。
中括弧で囲まれた部分が実行されるコードです。
Start-Process
はその名の通り指定したプロセスを起動するコマンドレット、-Verb RunAs
は管理者権限で起動するという意味のオプションです。
コマンドレット (Cmdlet) は PowerShell で利用可能なコマンドのことです。
hosts
ファイルを書き換えて保存するためには管理者権限が必要になるため、WSL (Windows Subsystem for Linux) を管理者権限で起動し、WSL 上の Vim で hosts
ファイルを開いています。
私は WSL のヘビーユーザーなのでこのようにしていますが、起動するエディタは好きなものを指定すればOKです。
例えば、メモ帳を起動する場合は以下のようになります。
Start-Process `
-FilePath notepad.exe `
-ArgumentList 'C:\Windows\System32\drivers\etc\hosts' `
-Verb RunAs
この例のように、コマンドの途中で改行する場合は行の末尾にバッククォート (`) を付けます。
ちなみに、PowerShell のコマンドレットや関数は基本的に「<動詞>-<名詞>」という名前付けになっており、前半の部分は使える動詞があらかじめ決められています。
使用が承認された動詞の一覧を取得するには、Get-Verb
関数を実行します。
PS C:\Users\xxx\Documents\WindowsPowerShell > Get-Verb
Verb Group
---- -----
Add Common
Clear Common
Close Common
Copy Common
Enter Common
Exit Common
Find Common
Format Common
Get Common
Hide Common
Join Common
Lock Common
Move Common
New Common
Open Common
︙
Get-EnvironmentVariables
function Get-EnvironmentVariables {
[Environment]::GetEnvironmentVariables().GetEnumerator() | Sort-Object -Property Key
}
次は Get-EnvironmentVariables
です。
こちらは Windows で設定されている環境変数を一覧で表示する関数になります。
[Environment]::GetEnvironmentVariables()
の部分で .NET Framework の Environment.GetEnvironmentVariables
メソッドを呼び出しています。
このように .NET Framework の豊富なAPIを利用できるのは、PowerShell の大きな強みの1つです。
さらに、PowerShell はオブジェクト指向に基づいており、コマンドの出力は .NET のオブジェクトとして表現され、そのままパイプラインで別のコマンドの入力として渡すことができます。
[Environment]::GetEnvironmentVariables()
の戻り値の型は Hashtable
で、そのままでも整形して見やすいフォーマットで表示してくれますが、環境変数名がアルファベット順に並んでおらず探しにくいです。
PS C:\Users\xxx\Documents\WindowsPowerShell > [Environment]::GetEnvironmentVariables().GetType()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Hashtable System.Object
PS C:\Users\xxx\Documents\WindowsPowerShell > [Environment]::GetEnvironmentVariables()
Name Value
---- -----
SESSIONNAME Console
ProgramFiles(x86) C:\Program Files (x86)
ProgramW6432 C:\Program Files
PROCESSOR_IDENTIFIER Intel64 Family 6 Model 167 Stepping 1, GenuineIntel
TMP C:\Users\xxx\AppData\Local\Temp
︙
そこで、GetEnumerator()
メソッドで Enumerator に変換したうえで Sort-Object
コマンドレットで名前順にソートして表示しています。
PS C:\Users\xxx\Documents\WindowsPowerShell > Get-EnvironmentVariables
Name Value
---- -----
ALLUSERSPROFILE C:\ProgramData
ANDROID_HOME C:\Users\xxx\AppData\Local\Android\Sdk
APPDATA C:\Users\xxx\AppData\Roaming
COMMONPROGRAMFILES C:\Program Files\Common Files
CommonProgramFiles(x86) C:\Program Files (x86)\Common Files
︙
Edit-EnvironmentVariables
function Edit-EnvironmentVariables {
Start-Process `
-FilePath rundll32.exe `
-ArgumentList 'sysdm.cpl,EditEnvironmentVariables' `
-Verb RunAs
}
次は Edit-EnvironmentVariables
です。
この関数を実行すると、Windows の環境変数のウィンドウを直接開くことができます。
こちらもシステム環境変数を編集するためには管理者権限で起動する必要があり、-Verb RunAs
オプションを付加しています。
Export-JavaHome
function Export-JavaHome ([int] $version) {
switch ($version) {
8 {
$dir = 'C:\langs\java\jdk8u312-b07'
}
11 {
$dir = 'C:\langs\java\jdk-11.0.14.101-hotspot'
}
17 {
$dir = 'C:\langs\java\jdk-17.0.2.8-hotspot'
}
default {
Write-Host 'Error: Invalid Java version'
return
}
}
Start-Process `
-FilePath powershell.exe `
-ArgumentList "-Command [Environment]::SetEnvironmentVariable('JAVA_HOME', '${dir}', [EnvironmentVariableTarget]::Machine)" `
-Verb RunAs
}
次は Export-JavaHome
です。
こちらは JAVA_HOME
環境変数を書き換えることで使用する Java のバージョンを切り替える関数になります。
WSL では SDKMAN! という JVM 系のソフトウェアのバージョン管理ができるコマンドラインツールを使用しているのですが、Windows には対応していないため PowerShell スクリプトで実現しています。
これまでの関数と異なる点として、関数名の後に括弧を置き引数を定義しています。
PowerShell では、PHP のように変数名の頭に $
を付けます。
この関数では int
型の引数を受け取り、switch
ステートメントで引数の値によって JAVA_HOME
に設定する値 (ディレクトリのパス) を切り替えています。
引数が 8, 11, 17 のいずれでもない場合はエラーメッセージを表示して処理を中断します。
PS C:\Users\xxx\Documents\WindowsPowerShell > Export-JavaHome 10
Error: Invalid Java version
なお、int
型に変換できない値を引数に指定して実行するとエラーになります。
PS C:\Users\xxx\Documents\WindowsPowerShell > Export-JavaHome a
Export-JavaHome : パラメーター 'version' の引数変換を処理できません。値 "a" を型 "System.Int32" に変換できません。エラー: "入力文字列の形式が正しくありません。"
発生場所 行:1 文字:17
+ Export-JavaHome a
+ ~
+ CategoryInfo : InvalidData: (:) [Export-JavaHome]、ParameterBindingArgumentTransformationException
+ FullyQualifiedErrorId : ParameterArgumentTransformationError,Export-JavaHome
プロンプトの設定
function prompt {
$principal = [Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()
if ($principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
$role = 'Admin'
} else {
$role = 'User'
}
'[{0}] {1} > ' -f $role, ((Get-Location).Path -split '::')[-1]
}
続いての prompt
関数は元々定義されている特殊な関数で、この関数の戻り値が PowerShell のプロンプトとして表示されます。(Bash でいうところの PS1
環境変数に相当します。)
私は WSL 上で Windows PowerShell を起動することが多いのですが、デフォルトの設定だと以下のようにプロンプトがかなり長くなり見づらいです。
$ powershell.exe
Windows PowerShell
Copyright (C) Microsoft Corporation. All rights reserved.
新しいクロスプラットフォームの PowerShell をお試しください https://aka.ms/pscore6
PS Microsoft.PowerShell.Core\FileSystem::\\wsl$\Ubuntu\home\xxx\pwsh> Get-Location
Path
----
Microsoft.PowerShell.Core\FileSystem::\\wsl$\Ubuntu\home\xxx\pwsh
そのため、ディレクトリのパスに ::
が含まれる場合は ::
以降を表示するようにしています。
また、.NET の Security.Principal
名前空間のクラスを使用して PowerShell が管理者権限で起動しているかどうかを判定し、プロンプトで判断できるようにしています。
以下は管理者権限で起動した場合のプロンプトです。
[Admin] C:\Windows\system32 >
関数の最終行の -f
は Format 演算子で、左辺の書式文字列に右辺の値を埋め込みます。
また、PowerShell の関数では return
キーワードを使用しなくても関数内で出力した値が呼び出し元に返却されるため、return
キーワードは省略しています。
エイリアスの設定
Set-Alias hosts Edit-Hosts
Set-Alias env Edit-EnvironmentVariables
続いて、Set-Alias
コマンドレットを使用して関数のエイリアスの設定を行っています。
この設定により、Edit-Hosts
と入力する代わりに hosts
と入力すれば管理者権限の Vim が起動します。
PowerShell のコマンドレットは文字数が多いため、デフォルトで設定されているエイリアスも多数存在します。
エイリアスの一覧を取得するには、Get-Alias
コマンドレットを実行します。
PS C:\Users\xxx\Documents\WindowsPowerShell > Get-Alias
CommandType Name Version Source
----------- ---- ------- ------
Alias % -> ForEach-Object
Alias ? -> Where-Object
Alias ac -> Add-Content
Alias asnp -> Add-PSSnapin
Alias cat -> Get-Content
Alias cd -> Set-Location
Alias CFS -> ConvertFrom-String 3.1.0.0 Microsoft.PowerShell.Utility
Alias chdir -> Set-Location
Alias clc -> Clear-Content
Alias clear -> Clear-Host
︙
最初に入力した cd
コマンドも Set-Location
コマンドレットのエイリアスであることが分かります。
他にも UNIX のコマンド名がエイリアスに設定されているものがたくさんあるので、コマンドレット名が分からない時はとりあえず UNIX コマンドをタイプしてみるとよいかもしれません。
キーバインドの設定
Set-PSReadlineKeyHandler -Chord Alt+b -Function BackwardWord
Set-PSReadlineKeyHandler -Chord Alt+d -Function KillWord
Set-PSReadlineKeyHandler -Chord Alt+f -Function NextWord
Set-PSReadlineKeyHandler -Chord Ctrl+a -Function BeginningOfLine
Set-PSReadlineKeyHandler -Chord Ctrl+d -Function DeleteCharOrExit
Set-PSReadlineKeyHandler -Chord Ctrl+e -Function EndOfLine
Set-PSReadlineKeyHandler -Chord Ctrl+k -Function KillLine
Set-PSReadlineKeyHandler -Chord Ctrl+u -Function BackwardKillLine
Set-PSReadlineKeyHandler -Chord Ctrl+w -Function BackwardKillWord
Set-PSReadlineKeyHandler -Chord Ctrl+Insert -Function Copy
Set-PSReadLineKeyHandler -Chord Tab -Function Complete
次に、PSReadLine
モジュールの Set-PSReadLineKeyHandler
コマンドレットを使用してキーバインドの設定を行っています。
PSReadLine
モジュールがインストールされていない場合は以下のコマンドでインストールします。
Install-Module -Name PSReadLine -Force
私は Bash のキーバインドに慣れているので、[Tab] で補完、[Ctrl] + [d] で exit など、できるだけ Bash のキーバインドに近付けるように設定をしています。
個人的には、この設定をするかどうかで PowerShell の使いやすさ・快適さが大きく異なってくると感じます。
その他
プロファイルでの設定は不要ですが、gsudo のインストールをおすすめします。
sudo
コマンドのようにコマンドを一度だけ Windows 管理者権限で実行したり、
su
コマンドのように現在のセッションを管理者権限に昇格したりすることができます。
WSL 上でも利用可能です。
$ gsudo powershell.exe
Windows PowerShell
Copyright (C) Microsoft Corporation. All rights reserved.
新しいクロスプラットフォームの PowerShell をお試しください https://aka.ms/pscore6
[Admin] C:\Users\morita\Documents\WindowsPowerShell > exit
$ powershell.exe
Windows PowerShell
Copyright (C) Microsoft Corporation. All rights reserved.
新しいクロスプラットフォームの PowerShell をお試しください https://aka.ms/pscore6
[User] C:\Users\morita\Documents\WindowsPowerShell > gsudo
[Admin] C:\Users\morita\Documents\WindowsPowerShell > exit
[User] C:\Users\morita\Documents\WindowsPowerShell >
主な参考文献
- PowerShell とは - PowerShell | Microsoft Docs
https://docs.microsoft.com/ja-jp/powershell/scripting/overview - 実行ポリシーについて - PowerShell | Microsoft Docs
https://docs.microsoft.com/ja-jp/powershell/module/microsoft.powershell.core/about/about_execution_policies - プロファイルについて - PowerShell | Microsoft Docs
https://docs.microsoft.com/ja-jp/powershell/module/microsoft.powershell.core/about/about_profiles - プロンプトについて - PowerShell | Microsoft Docs
https://docs.microsoft.com/ja-jp/powershell/module/microsoft.powershell.core/about/about_prompts - 演算子について - PowerShell | Microsoft Docs
https://docs.microsoft.com/ja-jp/powershell/module/microsoft.powershell.core/about/about_operators - PSReadLine について - PowerShell | Microsoft Docs
https://docs.microsoft.com/ja-jp/powershell/module/psreadline/about/about_psreadline